1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
use std::path::PathBuf;
use anyhow::anyhow;
use clap::arg;
use clap::Arg;
use clap::ArgMatches;
use serde::Deserialize;
use serde::Serialize;
use spacetimedb_lib::{TupleDef, TypeValue};
use tabled::builder::Builder;
use tabled::Style;
use crate::config::Config;
use crate::util::spacetime_dns;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StmtResultJson {
pub schema: TupleDef,
pub rows: Vec<Vec<TypeValue>>,
}
pub fn cli() -> clap::Command {
clap::Command::new("sql")
.about("Runs a SQL query on the database.")
.arg(Arg::new("database").required(true))
.arg(Arg::new("query").conflicts_with("filename").required(true))
.arg(
arg!(-f --filename <FILENAME> "filename")
.value_parser(clap::value_parser!(PathBuf))
.conflicts_with("query")
.required(true),
)
}
pub async fn exec(config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {
let database = args.get_one::<String>("database").unwrap();
let query = args.get_one::<String>("query").unwrap();
let address = if let Ok(address) = spacetime_dns(&config, database).await {
address
} else {
database.to_string()
};
let client = reqwest::Client::new();
let mut builder = client.post(format!("http://{}/database/sql/{}", config.host, address));
if let Some(identity_token) = config.get_default_identity_config() {
builder = builder.basic_auth("token", Some(identity_token.token.clone()));
} else {
println!("Missing identity credentials for identity.");
std::process::exit(0);
}
let builder = builder.body(query.to_owned());
let res = builder.send().await?;
let res = res.error_for_status()?;
let body = res.bytes().await.unwrap();
let json = String::from_utf8(body.to_vec()).unwrap();
let stmt_result_json: Vec<StmtResultJson> = serde_json::from_str(&json).unwrap();
let stmt_result = stmt_result_json.first();
if stmt_result.is_none() {
return Err(anyhow!("Invalid sql query."));
}
let stmt_result = stmt_result.unwrap();
let rows = &stmt_result.rows;
let schema = &stmt_result.schema;
let mut builder = Builder::default();
builder.set_columns(
schema
.elements
.iter()
.map(|e| e.name.as_ref().cloned().unwrap_or(format!("{}", e.tag))),
);
for row in rows {
builder.add_record(row.iter().map(ToString::to_string));
}
let table = builder.build().with(Style::psql());
println!("{}", table);
Ok(())
}