use std::collections::BTreeMap;
use std::sync::Arc;
use nodedb_client::NodeDb;
use nodedb_lite::{NodeDbLite, PagedbStorageMem};
use nodedb_types::result::QueryResult;
use nodedb_types::value::Value;
use tokio_postgres::{Client, NoTls, Row};
pub async fn open_lite() -> Arc<NodeDbLite<PagedbStorageMem>> {
let storage = PagedbStorageMem::open_in_memory()
.await
.expect("open_in_memory");
NodeDbLite::open(storage).await.expect("NodeDbLite::open")
}
pub struct OriginPgwire {
client: Client,
_conn_task: tokio::task::JoinHandle<()>,
}
impl OriginPgwire {
pub async fn connect() -> Self {
let conn_str = "host=127.0.0.1 port=6432 user=nodedb dbname=default sslmode=disable";
let (client, connection) = tokio_postgres::connect(conn_str, NoTls)
.await
.expect("connect to Origin pgwire");
let task = tokio::spawn(async move {
if let Err(e) = connection.await {
let _ = e;
}
});
OriginPgwire {
client,
_conn_task: task,
}
}
pub async fn query(&self, sql: &str) -> Vec<Row> {
self.client.query(sql, &[]).await.unwrap_or_else(|e| {
let detail = match e.as_db_error() {
Some(db) => format!("{}: {}", db.severity(), db.message()),
None => e.to_string(),
};
panic!("Origin query failed: {detail}\nSQL: {sql}")
})
}
pub async fn try_query(&self, sql: &str) -> Result<Vec<Row>, tokio_postgres::Error> {
self.client.query(sql, &[]).await
}
pub async fn poll_query(&self, sql: &str) -> Vec<Row> {
match self.try_query(sql).await {
Ok(rows) => rows,
Err(e) => {
let detail = match e.as_db_error() {
Some(db) => format!("{}: {}", db.severity(), db.message()),
None => e.to_string(),
};
eprintln!("poll_query transient error: {detail}\n SQL: {sql}");
Vec::new()
}
}
}
pub async fn execute(&self, sql: &str) {
self.client.execute(sql, &[]).await.unwrap_or_else(|e| {
let detail = if let Some(db) = e.as_db_error() {
format!(
"code={} message={} detail={:?}",
db.code().code(),
db.message(),
db.detail()
)
} else {
format!("{e:#}")
};
panic!("Origin execute failed: {detail}\nSQL: {sql}")
});
}
}
pub fn normalise_lite_row(result: &QueryResult, row_idx: usize) -> BTreeMap<String, String> {
result
.columns
.iter()
.zip(result.rows[row_idx].iter())
.map(|(col, val)| (col.clone(), value_to_string(val)))
.collect()
}
fn value_to_string(v: &Value) -> String {
match v {
Value::String(s) => s.clone(),
Value::Integer(i) => i.to_string(),
Value::Float(f) => f.to_string(),
Value::Bool(b) => b.to_string(),
Value::Null => "NULL".into(),
_ => format!("{v:?}"),
}
}
pub async fn assert_lite_unsupported(db: &Arc<NodeDbLite<PagedbStorageMem>>, sql: &str) {
let result = db.execute_sql(sql, &[]).await;
match result {
Err(e) => {
let display = e.to_string();
assert!(
display.contains("unsupported")
|| display.contains("Unsupported")
|| display.contains("not supported"),
"expected Unsupported error for SQL: {sql:?}\n got: {display}"
);
}
Ok(r) => {
panic!(
"expected Unsupported error but query succeeded for SQL: {sql:?}\n \
columns: {:?}\n rows: {}",
r.columns,
r.rows.len()
);
}
}
}