Skip to content

Commit 5e6ad1e

Browse files
wip
1 parent 6abaab8 commit 5e6ad1e

18 files changed

Lines changed: 513 additions & 172 deletions

File tree

crates/bench/benches/subscription.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use spacetimedb_bench::database::BenchDatabase as _;
1111
use spacetimedb_bench::spacetime_raw::SpacetimeRaw;
1212
use spacetimedb_client_api_messages::websocket::v1::BsatnFormat;
1313
use spacetimedb_datastore::execution_context::Workload;
14-
use spacetimedb_execution::pipelined::PipelinedProject;
14+
use spacetimedb_execution::{pipelined::PipelinedProject, ExecutionParams};
1515
use spacetimedb_primitives::{col_list, TableId};
1616
use spacetimedb_query::compile_subscription;
1717
use spacetimedb_sats::{bsatn, product, AlgebraicType, AlgebraicValue};
@@ -113,6 +113,7 @@ fn eval(c: &mut Criterion) {
113113
let auth = AuthCtx::for_testing();
114114
let schema_viewer = &SchemaViewer::new(&tx, &auth);
115115
let (plans, table_id, table_name, _) = compile_subscription(sql, schema_viewer, &auth).unwrap();
116+
let params = ExecutionParams::from_auth(&auth);
116117
let plans = plans
117118
.into_iter()
118119
.map(|plan| plan.optimize(&auth).unwrap())
@@ -126,6 +127,7 @@ fn eval(c: &mut Criterion) {
126127
table_id,
127128
table_name.clone(),
128129
&tx,
130+
&params,
129131
TableUpdateType::Subscribe,
130132
&bsatn_rlb_pool,
131133
));

crates/core/src/host/module_host.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub use crate::subscription::module_subscription_manager::TransactionOffset;
2727
use crate::subscription::row_list_builder_pool::{BsatnRowListBuilderPool, JsonRowListBuilderFakePool};
2828
use crate::subscription::tx::DeltaTx;
2929
use crate::subscription::websocket_building::{BuildableWebsocketFormat, RowListBuilderSource};
30-
use crate::subscription::{execute_plan, execute_plan_for_view};
30+
use crate::subscription::{execute_plan_for_view_with_params, execute_plan_with_params};
3131
use crate::util::jobs::{AllocatedJobCore, SingleThreadedExecutor};
3232
use crate::worker_metrics::WORKER_METRICS;
3333
use anyhow::Context;
@@ -52,6 +52,7 @@ use spacetimedb_datastore::locking_tx_datastore::{MutTxId, ViewCallInfo};
5252
use spacetimedb_datastore::traits::{IsolationLevel, Program, TxData};
5353
pub use spacetimedb_durability::{DurabilityExited, DurableOffset};
5454
use spacetimedb_execution::pipelined::PipelinedProject;
55+
use spacetimedb_execution::ExecutionParams;
5556
use spacetimedb_execution::RelValue;
5657
use spacetimedb_expr::expr::CollectViews;
5758
use spacetimedb_lib::db::raw_def::v9::Lifecycle;
@@ -3306,10 +3307,18 @@ impl ModuleHost {
33063307

33073308
let table_name = table_name.into();
33083309
let delta_tx = DeltaTx::from(tx);
3310+
let params = ExecutionParams::from_auth(auth);
33093311
let (rows, _, metrics) = if returns_view_table && num_private_cols > 0 {
3310-
execute_plan_for_view::<F>(optimized.iter(), num_cols, num_private_cols, &delta_tx, rlb_pool)
3312+
execute_plan_for_view_with_params::<F>(
3313+
optimized.iter(),
3314+
num_cols,
3315+
num_private_cols,
3316+
&delta_tx,
3317+
&params,
3318+
rlb_pool,
3319+
)
33113320
} else {
3312-
execute_plan::<F>(optimized.iter(), &delta_tx, rlb_pool)
3321+
execute_plan_with_params::<F>(optimized.iter(), &delta_tx, &params, rlb_pool)
33133322
}
33143323
.context("One-off queries are not allowed to modify the database")?;
33153324

crates/core/src/subscription/mod.rs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ use spacetimedb_client_api_messages::websocket::v1::{self as ws_v1};
1010
use spacetimedb_datastore::{
1111
db_metrics::DB_METRICS, execution_context::WorkloadType, locking_tx_datastore::datastore::MetricsRecorder,
1212
};
13-
use spacetimedb_execution::{pipelined::PipelinedProject, Datastore, DeltaStore, Row};
13+
use spacetimedb_execution::{pipelined::PipelinedProject, Datastore, DeltaStore, ExecutionParams, Row};
1414
use spacetimedb_lib::{metrics::ExecutionMetrics, Identity};
15+
use spacetimedb_physical_plan::plan::ParamResolver;
1516
use spacetimedb_primitives::{ColList, TableId};
1617
use spacetimedb_sats::bsatn::ToBsatn;
1718
use spacetimedb_sats::Serialize;
@@ -104,13 +105,35 @@ pub fn execute_plan_for_view<'p, F>(
104105
tx: &(impl Datastore + DeltaStore),
105106
rlb_pool: &impl RowListBuilderSource<F>,
106107
) -> Result<(F::List, u64, ExecutionMetrics)>
108+
where
109+
F: BuildableWebsocketFormat,
110+
{
111+
execute_plan_for_view_with_params(
112+
plan_fragments,
113+
num_cols,
114+
num_private_cols,
115+
tx,
116+
&ExecutionParams::empty(),
117+
rlb_pool,
118+
)
119+
}
120+
121+
/// Execute subscription query fragments over a view with runtime parameters.
122+
pub fn execute_plan_for_view_with_params<'p, F>(
123+
plan_fragments: impl IntoIterator<Item = &'p PipelinedProject>,
124+
num_cols: usize,
125+
num_private_cols: usize,
126+
tx: &(impl Datastore + DeltaStore),
127+
params: &impl ParamResolver,
128+
rlb_pool: &impl RowListBuilderSource<F>,
129+
) -> Result<(F::List, u64, ExecutionMetrics)>
107130
where
108131
F: BuildableWebsocketFormat,
109132
{
110133
build_list_with_executor(rlb_pool, |metrics, add| {
111134
let col_list = ColList::from_iter(num_private_cols..num_cols);
112135
for fragment in plan_fragments {
113-
fragment.execute(tx, metrics, &mut |row| match row {
136+
fragment.execute_with_params(tx, params, metrics, &mut |row| match row {
114137
Row::Ptr(ptr) => add(ptr.project_product(&col_list)?),
115138
Row::Ref(val) => add(val.project_product(&col_list)?),
116139
})?;
@@ -125,12 +148,25 @@ pub fn execute_plan<'p, F>(
125148
tx: &(impl Datastore + DeltaStore),
126149
rlb_pool: &impl RowListBuilderSource<F>,
127150
) -> Result<(F::List, u64, ExecutionMetrics)>
151+
where
152+
F: BuildableWebsocketFormat,
153+
{
154+
execute_plan_with_params(plan_fragments, tx, &ExecutionParams::empty(), rlb_pool)
155+
}
156+
157+
/// Execute a subscription query with runtime parameters.
158+
pub fn execute_plan_with_params<'p, F>(
159+
plan_fragments: impl IntoIterator<Item = &'p PipelinedProject>,
160+
tx: &(impl Datastore + DeltaStore),
161+
params: &impl ParamResolver,
162+
rlb_pool: &impl RowListBuilderSource<F>,
163+
) -> Result<(F::List, u64, ExecutionMetrics)>
128164
where
129165
F: BuildableWebsocketFormat,
130166
{
131167
build_list_with_executor(rlb_pool, |metrics, add| {
132168
for fragment in plan_fragments {
133-
fragment.execute(tx, metrics, add)?;
169+
fragment.execute_with_params(tx, params, metrics, add)?;
134170
}
135171
Ok(())
136172
})
@@ -208,14 +244,15 @@ pub fn collect_table_update_for_view<'p, Tx, F>(
208244
table_id: TableId,
209245
table_name: TableName,
210246
tx: &Tx,
247+
params: &impl ParamResolver,
211248
update_type: TableUpdateType,
212249
rlb_pool: &impl RowListBuilderSource<F>,
213250
) -> Result<(ws_v1::TableUpdate<F>, ExecutionMetrics)>
214251
where
215252
Tx: Datastore + DeltaStore,
216253
F: BuildableWebsocketFormat,
217254
{
218-
execute_plan_for_view::<F>(plan_fragments, num_cols, num_private_cols, tx, rlb_pool).map(
255+
execute_plan_for_view_with_params::<F>(plan_fragments, num_cols, num_private_cols, tx, params, rlb_pool).map(
219256
|(rows, num_rows, metrics)| table_update_from_rows(rows, num_rows, metrics, table_id, table_name, update_type),
220257
)
221258
}
@@ -226,13 +263,14 @@ pub fn collect_table_update<'p, F>(
226263
table_id: TableId,
227264
table_name: TableName,
228265
tx: &(impl Datastore + DeltaStore),
266+
params: &impl ParamResolver,
229267
update_type: TableUpdateType,
230268
rlb_pool: &impl RowListBuilderSource<F>,
231269
) -> Result<(ws_v1::TableUpdate<F>, ExecutionMetrics)>
232270
where
233271
F: BuildableWebsocketFormat,
234272
{
235-
execute_plan::<F>(plan_fragments, tx, rlb_pool).map(|(rows, num_rows, metrics)| {
273+
execute_plan_with_params::<F>(plan_fragments, tx, params, rlb_pool).map(|(rows, num_rows, metrics)| {
236274
table_update_from_rows(rows, num_rows, metrics, table_id, table_name, update_type)
237275
})
238276
}
@@ -265,6 +303,7 @@ pub fn execute_plans<F: BuildableWebsocketFormat>(
265303
table_id,
266304
table_name.clone(),
267305
tx,
306+
plan.params(),
268307
update_type,
269308
rlb_pool,
270309
)?
@@ -274,6 +313,7 @@ pub fn execute_plans<F: BuildableWebsocketFormat>(
274313
table_id,
275314
table_name.clone(),
276315
tx,
316+
plan.params(),
277317
update_type,
278318
rlb_pool,
279319
)?

crates/core/src/subscription/module_subscription_actor.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use spacetimedb_datastore::locking_tx_datastore::datastore::TxMetrics;
3535
use spacetimedb_datastore::locking_tx_datastore::{MutTxId, TxId};
3636
use spacetimedb_datastore::traits::{IsolationLevel, TxData};
3737
use spacetimedb_durability::TxOffset;
38+
use spacetimedb_execution::ExecutionParams;
3839
use spacetimedb_expr::expr::CollectViews;
3940
use spacetimedb_lib::identity::RequestId;
4041
use spacetimedb_lib::metrics::ExecutionMetrics;
@@ -467,6 +468,7 @@ impl ModuleSubscriptions {
467468
.unwrap_or_default();
468469

469470
let tx = DeltaTx::from(tx);
471+
let params = ExecutionParams::from_sender(sender.id.identity);
470472

471473
// TODO: See the comment on `collect_table_update_for_view`.
472474
// The following view and non-view branches should be merged together,
@@ -479,6 +481,7 @@ impl ModuleSubscriptions {
479481
table_id,
480482
table_name.clone(),
481483
&tx,
484+
&params,
482485
update_type,
483486
&self.bsatn_rlb_pool,
484487
)
@@ -488,6 +491,7 @@ impl ModuleSubscriptions {
488491
table_id,
489492
table_name.clone(),
490493
&tx,
494+
&params,
491495
update_type,
492496
&self.bsatn_rlb_pool,
493497
)
@@ -499,6 +503,7 @@ impl ModuleSubscriptions {
499503
table_id,
500504
table_name,
501505
&tx,
506+
&params,
502507
update_type,
503508
&JsonRowListBuilderFakePool,
504509
)
@@ -508,6 +513,7 @@ impl ModuleSubscriptions {
508513
table_id,
509514
table_name,
510515
&tx,
516+
&params,
511517
update_type,
512518
&JsonRowListBuilderFakePool,
513519
)

crates/execution/src/dml.rs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use anyhow::Result;
22
use spacetimedb_lib::{metrics::ExecutionMetrics, AlgebraicValue, ProductValue};
33
use spacetimedb_physical_plan::dml::{DeletePlan, InsertPlan, MutationPlan, UpdatePlan};
4+
use spacetimedb_physical_plan::plan::ParamResolver;
45
use spacetimedb_primitives::{ColId, TableId};
56
use spacetimedb_sats::size_of::SizeOf;
67

7-
use crate::{pipelined::PipelinedProject, Datastore, DeltaStore};
8+
use crate::{pipelined::PipelinedProject, Datastore, DeltaStore, ExecutionParams};
89

910
/// A mutable datastore can read as well as insert and delete rows
1011
pub trait MutDatastore: Datastore + DeltaStore {
@@ -31,10 +32,19 @@ impl From<MutationPlan> for MutExecutor {
3132

3233
impl MutExecutor {
3334
pub fn execute<Tx: MutDatastore>(&self, tx: &mut Tx, metrics: &mut ExecutionMetrics) -> Result<()> {
35+
self.execute_with_params(tx, &ExecutionParams::empty(), metrics)
36+
}
37+
38+
pub fn execute_with_params<Tx: MutDatastore>(
39+
&self,
40+
tx: &mut Tx,
41+
params: &impl ParamResolver,
42+
metrics: &mut ExecutionMetrics,
43+
) -> Result<()> {
3444
match self {
3545
Self::Insert(exec) => exec.execute(tx, metrics),
36-
Self::Delete(exec) => exec.execute(tx, metrics),
37-
Self::Update(exec) => exec.execute(tx, metrics),
46+
Self::Delete(exec) => exec.execute_with_params(tx, params, metrics),
47+
Self::Update(exec) => exec.execute_with_params(tx, params, metrics),
3848
}
3949
}
4050
}
@@ -84,10 +94,15 @@ impl From<DeletePlan> for DeleteExecutor {
8494
}
8595

8696
impl DeleteExecutor {
87-
fn execute<Tx: MutDatastore>(&self, tx: &mut Tx, metrics: &mut ExecutionMetrics) -> Result<()> {
97+
fn execute_with_params<Tx: MutDatastore>(
98+
&self,
99+
tx: &mut Tx,
100+
params: &impl ParamResolver,
101+
metrics: &mut ExecutionMetrics,
102+
) -> Result<()> {
88103
// TODO: Delete by row id instead of product value
89104
let mut deletes = vec![];
90-
self.filter.execute(tx, metrics, &mut |row| {
105+
self.filter.execute_with_params(tx, params, metrics, &mut |row| {
91106
deletes.push(row.to_product_value());
92107
Ok(())
93108
})?;
@@ -122,9 +137,14 @@ impl From<UpdatePlan> for UpdateExecutor {
122137
}
123138

124139
impl UpdateExecutor {
125-
fn execute<Tx: MutDatastore>(&self, tx: &mut Tx, metrics: &mut ExecutionMetrics) -> Result<()> {
140+
fn execute_with_params<Tx: MutDatastore>(
141+
&self,
142+
tx: &mut Tx,
143+
params: &impl ParamResolver,
144+
metrics: &mut ExecutionMetrics,
145+
) -> Result<()> {
126146
let mut deletes = vec![];
127-
self.filter.execute(tx, metrics, &mut |row| {
147+
self.filter.execute_with_params(tx, params, metrics, &mut |row| {
128148
deletes.push(row.to_product_value());
129149
Ok(())
130150
})?;

crates/execution/src/lib.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,53 @@
11
use anyhow::Result;
22
use core::hash::{Hash, Hasher};
33
use core::ops::RangeBounds;
4-
use spacetimedb_lib::query::Delta;
5-
use spacetimedb_physical_plan::plan::{ProjectField, TupleField};
4+
use spacetimedb_lib::{identity::AuthCtx, query::Delta, AlgebraicType, Identity};
5+
use spacetimedb_physical_plan::plan::{ParamResolver, ProjectField, TupleField};
66
use spacetimedb_primitives::{ColList, IndexId, TableId};
77
use spacetimedb_sats::bsatn::{BufReservedFill, EncodeError, ToBsatn};
88
use spacetimedb_sats::buffer::BufWriter;
99
use spacetimedb_sats::product_value::InvalidFieldError;
1010
use spacetimedb_sats::{impl_serialize, AlgebraicValue, ProductValue};
11+
use spacetimedb_sql_parser::ast::Parameter;
1112
use spacetimedb_table::{static_assert_size, table::RowRef};
1213

1314
pub mod dml;
1415
pub mod pipelined;
1516

17+
#[derive(Debug, Clone, Copy, Default)]
18+
pub struct ExecutionParams {
19+
sender: Option<Identity>,
20+
}
21+
22+
impl ExecutionParams {
23+
pub fn empty() -> Self {
24+
Self { sender: None }
25+
}
26+
27+
pub fn from_sender(sender: Identity) -> Self {
28+
Self { sender: Some(sender) }
29+
}
30+
31+
pub fn from_auth(auth: &AuthCtx) -> Self {
32+
Self::from_sender(auth.caller())
33+
}
34+
}
35+
36+
impl ParamResolver for ExecutionParams {
37+
fn resolve_param(&self, param: Parameter, ty: &AlgebraicType) -> AlgebraicValue {
38+
match param {
39+
Parameter::Sender if ty.is_identity() => self.sender.expect("missing sender binding for :sender").into(),
40+
Parameter::Sender if ty.is_bytes() => AlgebraicValue::Bytes(
41+
self.sender
42+
.expect("missing sender binding for :sender")
43+
.to_be_byte_array()
44+
.into(),
45+
),
46+
Parameter::Sender => panic!("unsupported type for :sender: {ty:?}"),
47+
}
48+
}
49+
}
50+
1651
pub trait Datastore {
1752
/// Iterator type for table scans
1853
type TableIter<'a>: Iterator<Item = RowRef<'a>> + 'a

0 commit comments

Comments
 (0)