-
Notifications
You must be signed in to change notification settings - Fork 1k
Expand file tree
/
Copy pathlib.rs
More file actions
99 lines (87 loc) · 3.39 KB
/
Copy pathlib.rs
File metadata and controls
99 lines (87 loc) · 3.39 KB
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
93
94
95
96
97
98
99
use anyhow::{bail, Result};
use spacetimedb_execution::{
dml::{MutDatastore, MutExecutor},
pipelined::ProjectListExecutor,
Datastore, DeltaStore,
};
use spacetimedb_expr::{
check::{parse_and_type_sub, SchemaView},
expr::ProjectList,
rls::{resolve_views_for_sql, resolve_views_for_sub},
statement::{parse_and_type_sql, Statement, DML},
};
use spacetimedb_lib::{identity::AuthCtx, metrics::ExecutionMetrics, ProductValue};
use spacetimedb_physical_plan::{
compile::{compile_dml_plan, compile_select, compile_select_list},
plan::{ProjectListPlan, ProjectPlan},
};
use spacetimedb_primitives::TableId;
/// DIRTY HACK ALERT: Maximum allowed length, in UTF-8 bytes, of SQL queries.
/// Any query longer than this will be rejected.
/// This prevents a stack overflow when compiling queries with deeply-nested `AND` and `OR` conditions.
const MAX_SQL_LENGTH: usize = 50_000;
pub fn compile_subscription(
sql: &str,
tx: &impl SchemaView,
auth: &AuthCtx,
) -> Result<(Vec<ProjectPlan>, TableId, Box<str>, bool)> {
if sql.len() > MAX_SQL_LENGTH {
bail!("SQL query exceeds maximum allowed length: \"{sql:.120}...\"")
}
let (plan, mut has_param) = parse_and_type_sub(sql, tx, auth)?;
let Some(return_id) = plan.return_table_id() else {
bail!("Failed to determine TableId for query")
};
let Some(return_name) = tx.schema_for_table(return_id).map(|schema| schema.table_name.clone()) else {
bail!("TableId `{return_id}` does not exist")
};
// Resolve any RLS filters
let plan_fragments = resolve_views_for_sub(tx, plan, auth, &mut has_param)?
.into_iter()
.map(compile_select)
.collect::<Vec<_>>();
// Does this subscription read from a client-specific view?
// If so, it is as if the view is parameterized by `:sender`.
// We must know this in order to generate the correct query hash.
let reads_view = plan_fragments.iter().any(|plan| plan.reads_from_view(false));
Ok((plan_fragments, return_id, return_name, has_param || reads_view))
}
/// A utility for parsing and type checking a sql statement
pub fn compile_sql_stmt(sql: &str, tx: &impl SchemaView, auth: &AuthCtx) -> Result<Statement> {
if sql.len() > MAX_SQL_LENGTH {
bail!("SQL query exceeds maximum allowed length: \"{sql:.120}...\"")
}
match parse_and_type_sql(sql, tx, auth)? {
stmt @ Statement::DML(_) => Ok(stmt),
Statement::Select(expr) => Ok(Statement::Select(resolve_views_for_sql(tx, expr, auth)?)),
}
}
/// A utility for executing a sql select statement
pub fn execute_select_stmt<Tx: Datastore + DeltaStore>(
auth: &AuthCtx,
stmt: ProjectList,
tx: &Tx,
metrics: &mut ExecutionMetrics,
check_row_limit: impl Fn(ProjectListPlan) -> Result<ProjectListPlan>,
) -> Result<Vec<ProductValue>> {
let plan = compile_select_list(stmt).optimize(auth)?;
let plan = check_row_limit(plan)?;
let plan = ProjectListExecutor::from(plan);
let mut rows = vec![];
plan.execute(tx, metrics, &mut |row| {
rows.push(row);
Ok(())
})?;
Ok(rows)
}
/// A utility for executing a sql dml statement
pub fn execute_dml_stmt<Tx: MutDatastore>(
auth: &AuthCtx,
stmt: DML,
tx: &mut Tx,
metrics: &mut ExecutionMetrics,
) -> Result<()> {
let plan = compile_dml_plan(stmt).optimize(auth)?;
let plan = MutExecutor::from(plan);
plan.execute(tx, metrics)
}