Files
daqbuffer/crates/dbconn/src/search.rs
T
2024-08-16 10:53:32 +02:00

305 lines
10 KiB
Rust

use crate::create_connection;
use crate::worker::PgQueue;
use crate::ErrConv;
use err::Error;
use netpod::log::*;
use netpod::ChannelArchiver;
use netpod::ChannelSearchQuery;
use netpod::ChannelSearchResult;
use netpod::ChannelSearchSingleResult;
use netpod::Database;
use netpod::NodeConfigCached;
use netpod::ScalarType;
use netpod::Shape;
use serde_json::Value as JsVal;
use tokio_postgres::Client as PgClient;
pub async fn search_channel_databuffer(
query: ChannelSearchQuery,
backend: &str,
node_config: &NodeConfigCached,
) -> Result<ChannelSearchResult, Error> {
let empty = if !query.name_regex.is_empty() {
false
} else if !query.source_regex.is_empty() {
false
} else if !query.description_regex.is_empty() {
false
} else {
true
};
if empty {
let ret = ChannelSearchResult { channels: Vec::new() };
return Ok(ret);
}
let sql = concat!(
"select",
" channel_id, channel_name, source_name,",
" dtype, shape, unit, description, channel_backend",
" from searchext($1, $2, $3, $4)",
" where channel_backend = $5"
);
let (pg, _pgjh) = create_connection(&node_config.node_config.cluster.database).await?;
let rows = pg
.query(
sql,
&[
&query.name_regex,
&query.source_regex,
&query.description_regex,
&"asc",
&backend,
],
)
.await
.err_conv()?;
let mut res = Vec::new();
for row in rows {
let shapedb: Option<serde_json::Value> = row.get(4);
let shape = match &shapedb {
Some(top) => match top {
serde_json::Value::Null => Vec::new(),
serde_json::Value::Array(items) => {
let mut a = Vec::new();
for item in items {
match item {
serde_json::Value::Number(n) => match n.as_i64() {
Some(n) => {
a.push(n as u32);
}
None => return Err(Error::with_msg(format!("can not understand shape {:?}", shapedb))),
},
_ => return Err(Error::with_msg(format!("can not understand shape {:?}", shapedb))),
}
}
a
}
_ => return Err(Error::with_msg(format!("can not understand shape {:?}", shapedb))),
},
None => Vec::new(),
};
let ty: String = row.get(3);
let k = ChannelSearchSingleResult {
backend: row.get(7),
name: row.get(1),
series: row.get::<_, i64>(0) as u64,
source: row.get(2),
ty,
shape: shape,
unit: row.get(5),
description: row.get(6),
is_api_0: None,
};
res.push(k);
}
let res = if let Some(backend) = query.backend.as_ref() {
res.into_iter().filter(|x| x.backend == *backend).collect()
} else {
res
};
let ret = ChannelSearchResult { channels: res };
Ok(ret)
}
pub(super) async fn search_channel_scylla(
query: ChannelSearchQuery,
pgc: &PgClient,
) -> Result<ChannelSearchResult, Error> {
let empty = if !query.name_regex.is_empty() { false } else { true };
if empty {
let ret = ChannelSearchResult { channels: Vec::new() };
return Ok(ret);
}
let ch_kind: i16 = query.kind.to_db_i16();
let (cb1, cb2) = if let Some(x) = query.backend.as_ref() {
(false, x.as_str())
} else {
(false, "----------")
};
let regop = if query.icase { "~*" } else { "~" };
let sql = &format!(
concat!(
"select",
" series, facility, channel, scalar_type, shape_dims",
" from series_by_channel",
" where kind = $1",
" and channel {} $2",
" and ($3 or facility = $4)",
" limit 400000",
),
regop
);
let params: &[&(dyn tokio_postgres::types::ToSql + Sync)] = &[&ch_kind, &query.name_regex, &cb1, &cb2];
info!("search_channel_scylla {:?}", params);
let rows = pgc.query(sql, params).await.err_conv()?;
let mut res = Vec::new();
for row in rows {
let series: i64 = row.get(0);
let series = series as u64;
let backend: String = row.get(1);
let channel: String = row.get(2);
let a: i32 = row.get(3);
// TODO count the failure cases
if let Ok(scalar_type) = ScalarType::from_scylla_i32(a) {
let a: Vec<i32> = row.get(4);
if let Ok(shape) = Shape::from_scylla_shape_dims(&a) {
let k = ChannelSearchSingleResult {
backend,
name: channel,
series,
source: "".into(),
ty: scalar_type.to_variant_str().into(),
shape: shape.to_scylla_vec().into_iter().map(|x| x as u32).collect(),
unit: "".into(),
description: "".into(),
is_api_0: None,
};
res.push(k);
} else {
netpod::log::warn!("unknown shape {a:?}");
}
} else {
netpod::log::warn!("unknown scalar_type {a:?}");
}
}
let ret = ChannelSearchResult { channels: res };
Ok(ret)
}
async fn search_channel_archeng(
query: ChannelSearchQuery,
backend: String,
_conf: &ChannelArchiver,
database: &Database,
) -> Result<ChannelSearchResult, Error> {
// Channel archiver provides only channel name. Also, search criteria are currently ANDed.
// Therefore search only if user only provides a name criterion.
let empty = if !query.source_regex.is_empty() {
true
} else if !query.description_regex.is_empty() {
true
} else if query.name_regex.is_empty() {
true
} else {
false
};
if empty {
let ret = ChannelSearchResult { channels: Vec::new() };
return Ok(ret);
}
let sql = format!(concat!(
"select c.name, c.config",
" from channels c",
" where c.name ~* $1",
" order by c.name",
" limit 100"
));
let (cl, _pgjh) = create_connection(database).await?;
let rows = cl.query(sql.as_str(), &[&query.name_regex]).await.err_conv()?;
let mut res = Vec::new();
for row in rows {
let name: String = row.get(0);
let config: JsVal = row.get(1);
let st = match config.get("scalarType") {
Some(k) => match k {
JsVal::String(k) => match k.as_str() {
"U8" => "Uint8",
"U16" => "Uint16",
"U32" => "Uint32",
"U64" => "Uint64",
"I8" => "Int8",
"I16" => "Int16",
"I32" => "Int32",
"I64" => "Int64",
"F32" => "Float32",
"F64" => "Float64",
_ => k,
}
.into(),
_ => "",
},
None => "",
};
let shape = match config.get("shape") {
Some(k) => match k {
JsVal::String(k) => {
if k == "Scalar" {
Vec::new()
} else {
return Err(Error::with_msg_no_trace(format!(
"search_channel_archeng can not understand {:?}",
config
)));
}
}
JsVal::Object(k) => match k.get("Wave") {
Some(k) => match k {
JsVal::Number(k) => {
vec![k.as_i64().unwrap_or(u32::MAX as i64) as u32]
}
_ => {
return Err(Error::with_msg_no_trace(format!(
"search_channel_archeng can not understand {:?}",
config
)));
}
},
None => {
return Err(Error::with_msg_no_trace(format!(
"search_channel_archeng can not understand {:?}",
config
)));
}
},
_ => {
return Err(Error::with_msg_no_trace(format!(
"search_channel_archeng can not understand {:?}",
config
)));
}
},
None => Vec::new(),
};
let k = ChannelSearchSingleResult {
backend: backend.clone(),
name,
// TODO provide a unique id also within this backend:
series: 0,
source: String::new(),
ty: st.into(),
shape,
unit: String::new(),
description: String::new(),
is_api_0: None,
};
res.push(k);
}
let ret = ChannelSearchResult { channels: res };
Ok(ret)
}
pub async fn search_channel(
query: ChannelSearchQuery,
pgqueue: &PgQueue,
ncc: &NodeConfigCached,
) -> Result<ChannelSearchResult, Error> {
let backend = &ncc.node_config.cluster.backend;
let pgconf = &ncc.node_config.cluster.database;
let mut query = query;
query.backend = Some(backend.into());
if let Some(_scyconf) = ncc.node_config.cluster.scylla_st() {
pgqueue
.search_channel_scylla(query)
.await
.map_err(|e| Error::with_msg_no_trace(format!("db worker error {e}")))?
// search_channel_scylla(query, backend, pgconf).await
} else if let Some(conf) = ncc.node.channel_archiver.as_ref() {
search_channel_archeng(query, backend.clone(), conf, pgconf).await
} else if let Some(_conf) = ncc.node.archiver_appliance.as_ref() {
// TODO
err::todoval()
} else {
search_channel_databuffer(query, backend, ncc).await
}
}