Skip to content

Commit a284b57

Browse files
committed
CLI: namespace support for spacetime call and spacetime sql
1 parent f075552 commit a284b57

4 files changed

Lines changed: 58 additions & 37 deletions

File tree

crates/cli/src/api.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,10 @@ impl ClientApi {
7373
}
7474

7575
pub async fn call(&self, reducer_name: &str, arg_json: String) -> anyhow::Result<reqwest::Response> {
76+
let encoded = reducer_name.replace('/', "%2F");
7677
Ok(self
7778
.client
78-
.post(self.con.db_uri("call") + "/" + reducer_name)
79+
.post(self.con.db_uri("call") + "/" + &encoded)
7980
.header(http::header::CONTENT_TYPE, "application/json")
8081
.body(arg_json)
8182
.send()

crates/cli/src/subcommands/call.rs

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ use clap::{Arg, ArgMatches};
1010
use convert_case::{Case, Casing};
1111
use core::ops::Deref;
1212
use itertools::Itertools;
13+
use spacetimedb_lib::db::raw_def::v9::{RawMiscModuleExportV9, RawModuleDefV9, RawProcedureDefV9, RawReducerDefV9};
1314
use spacetimedb_lib::sats::{self, AlgebraicType, Typespace};
1415
use spacetimedb_lib::{Identity, ProductTypeElement};
15-
use spacetimedb_schema::def::{ModuleDef, ProcedureDef, ReducerDef};
1616
use std::fmt::Write;
1717

1818
pub fn cli() -> clap::Command {
@@ -38,21 +38,21 @@ pub fn cli() -> clap::Command {
3838
}
3939

4040
enum CallDef<'a> {
41-
Reducer(&'a ReducerDef),
42-
Procedure(&'a ProcedureDef),
41+
Reducer(&'a RawReducerDefV9),
42+
Procedure(&'a RawProcedureDefV9),
4343
}
4444

4545
impl<'a> CallDef<'a> {
4646
fn params(&self) -> &'a sats::ProductType {
4747
match self {
48-
CallDef::Reducer(reducer_def) => &reducer_def.params,
49-
CallDef::Procedure(procedure_def) => &procedure_def.params,
48+
CallDef::Reducer(r) => &r.params,
49+
CallDef::Procedure(p) => &p.params,
5050
}
5151
}
5252
fn name(&self) -> &str {
5353
match self {
54-
CallDef::Reducer(reducer_def) => &reducer_def.name,
55-
CallDef::Procedure(procedure_def) => &procedure_def.name,
54+
CallDef::Reducer(r) => r.name.as_ref(),
55+
CallDef::Procedure(p) => p.name.as_ref(),
5656
}
5757
}
5858
fn kind(&self) -> &str {
@@ -101,21 +101,31 @@ pub async fn exec(config: Config, args: &ArgMatches) -> Result<(), Error> {
101101
let database_identity = api.con.database_identity;
102102
let database = &api.con.database;
103103

104-
let module_def: ModuleDef = api.module_def().await?.try_into()?;
104+
let raw = api.module_def().await?;
105105

106-
let call_def = match module_def.reducer(&**reducer_procedure_name) {
106+
let call_def = match raw.reducers.iter().find(|r| r.name.as_ref() == reducer_procedure_name.as_str()) {
107107
Some(reducer_def) => CallDef::Reducer(reducer_def),
108-
None => match module_def.procedure(&**reducer_procedure_name) {
109-
Some(procedure_def) => CallDef::Procedure(procedure_def),
110-
None => {
111-
return Err(anyhow::Error::msg(no_such_reducer_or_procedure(
112-
&database_identity,
113-
database,
114-
reducer_procedure_name,
115-
&module_def,
116-
)));
108+
None => {
109+
let procedure = raw.misc_exports.iter().find_map(|e| match e {
110+
RawMiscModuleExportV9::Procedure(p)
111+
if p.name.as_ref() == reducer_procedure_name.as_str() =>
112+
{
113+
Some(p)
114+
}
115+
_ => None,
116+
});
117+
match procedure {
118+
Some(procedure_def) => CallDef::Procedure(procedure_def),
119+
None => {
120+
return Err(anyhow::Error::msg(no_such_reducer_or_procedure(
121+
&database_identity,
122+
database,
123+
reducer_procedure_name,
124+
&raw,
125+
)));
126+
}
117127
}
118-
},
128+
}
119129
};
120130

121131
// String quote any arguments that should be quoted
@@ -141,9 +151,9 @@ pub async fn exec(config: Config, args: &ArgMatches) -> Result<(), Error> {
141151

142152
let error_msg =
143153
if response_text.starts_with("no such reducer") || response_text.starts_with("no such procedure") {
144-
no_such_reducer_or_procedure(&database_identity, database, reducer_procedure_name, &module_def)
154+
no_such_reducer_or_procedure(&database_identity, database, reducer_procedure_name, &raw)
145155
} else if response_text.starts_with("invalid arguments") {
146-
invalid_arguments(&database_identity, database, &response_text, &module_def, call_def)
156+
invalid_arguments(&database_identity, database, &response_text, &raw.typespace, call_def)
147157
} else {
148158
return error;
149159
};
@@ -160,7 +170,7 @@ pub async fn exec(config: Config, args: &ArgMatches) -> Result<(), Error> {
160170
}
161171

162172
/// Returns an error message for when `reducer` is called with wrong arguments.
163-
fn invalid_arguments(identity: &Identity, db: &str, text: &str, module_def: &ModuleDef, call_def: CallDef) -> String {
173+
fn invalid_arguments(identity: &Identity, db: &str, text: &str, typespace: &Typespace, call_def: CallDef) -> String {
164174
let mut error = format!(
165175
"Invalid arguments provided for {} `{}` for database `{}` resolving to identity `{}`.",
166176
call_def.kind(),
@@ -181,7 +191,7 @@ fn invalid_arguments(identity: &Identity, db: &str, text: &str, module_def: &Mod
181191
error,
182192
"\n\nThe {} has the following signature:\n\t{}",
183193
call_def.kind(),
184-
CallSignature(module_def.typespace().with_type(&call_def))
194+
CallSignature(typespace.with_type(&call_def))
185195
)
186196
.unwrap();
187197

@@ -235,12 +245,12 @@ impl std::fmt::Display for CallSignature<'_> {
235245
}
236246

237247
/// Returns an error message for when `reducer` or `procedure` does not exist in `db`.
238-
fn no_such_reducer_or_procedure(database_identity: &Identity, db: &str, name: &str, module_def: &ModuleDef) -> String {
248+
fn no_such_reducer_or_procedure(database_identity: &Identity, db: &str, name: &str, raw: &RawModuleDefV9) -> String {
239249
let mut error = format!(
240250
"No such reducer OR procedure `{name}` for database `{db}` resolving to identity `{database_identity}`."
241251
);
242252

243-
add_reducer_procedure_ctx_to_err(&mut error, module_def, name);
253+
add_reducer_procedure_ctx_to_err(&mut error, raw, name);
244254

245255
error
246256
}
@@ -249,16 +259,21 @@ const CALL_PRINT_LIMIT: usize = 10;
249259

250260
/// Provided the schema for the database,
251261
/// decorate `error` with more helpful info about reducers and procedures.
252-
fn add_reducer_procedure_ctx_to_err(error: &mut String, module_def: &ModuleDef, reducer_name: &str) {
253-
let reducers = module_def
254-
.reducers()
255-
.filter(|reducer| reducer.lifecycle.is_none())
256-
.map(|reducer| &*reducer.name)
262+
fn add_reducer_procedure_ctx_to_err(error: &mut String, raw: &RawModuleDefV9, reducer_name: &str) {
263+
let reducers = raw
264+
.reducers
265+
.iter()
266+
.filter(|r| r.lifecycle.is_none())
267+
.map(|r| r.name.as_ref())
257268
.collect::<Vec<_>>();
258269

259-
let procedures = module_def
260-
.procedures()
261-
.map(|reducer| &*reducer.name)
270+
let procedures = raw
271+
.misc_exports
272+
.iter()
273+
.filter_map(|e| match e {
274+
RawMiscModuleExportV9::Procedure(p) => Some(p.name.as_ref()),
275+
_ => None,
276+
})
262277
.collect::<Vec<_>>();
263278

264279
if let Some(best) = find_best_match_for_name(&reducers, reducer_name, None) {

crates/sql-parser/src/parser/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use sqlparser::ast::{
55
WildcardAdditionalOptions,
66
};
77

8+
use spacetimedb_lib::sats::raw_identifier::RawIdentifier;
9+
810
use crate::ast::{
911
BinOp, LogOp, Parameter, Project, ProjectElem, ProjectExpr, SqlExpr, SqlFrom, SqlIdent, SqlJoin, SqlLiteral,
1012
};
@@ -348,5 +350,8 @@ pub(crate) fn parse_parts(mut parts: Vec<Ident>) -> SqlParseResult<SqlIdent> {
348350
if parts.len() == 1 {
349351
return Ok(parts.swap_remove(0).into());
350352
}
351-
Err(SqlUnsupported::MultiPartName(ObjectName(parts)).into())
353+
// Join multi-part names (e.g. `lib.library_table`) with dots to match
354+
// namespace-prefixed table names stored in the catalog.
355+
let joined = parts.iter().map(|p| p.value.as_str()).collect::<Vec<_>>().join(".");
356+
Ok(SqlIdent(RawIdentifier::new(joined)))
352357
}

crates/sql-parser/src/parser/sql.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,8 +403,6 @@ mod tests {
403403
for sql in [
404404
// FROM is required
405405
"select 1",
406-
// Multi-part table names
407-
"select a from s.t",
408406
// Bit-string literals
409407
"select * from t where a = B'1010'",
410408
// Wildcard with non-wildcard projections
@@ -430,6 +428,8 @@ mod tests {
430428
fn supported() {
431429
for sql in [
432430
"select a from t",
431+
// Multi-part names are joined with dots for namespace-qualified tables
432+
"select a from s.t",
433433
"select a from t where x = :sender",
434434
"select count(*) as n from t",
435435
"select count(*) as n from t join s on t.id = s.id where s.x = 1",

0 commit comments

Comments
 (0)