Merge 'translate: return parse errors for unsupported features instead of silently ignoring' from Jussi Saurio

Closes #1510

Closes #2505
This commit is contained in:
Jussi Saurio
2025-08-08 15:56:13 +03:00
committed by GitHub
4 changed files with 108 additions and 51 deletions

View File

@@ -51,7 +51,7 @@ use std::rc::Rc;
use std::sync::Arc;
use tracing::{instrument, Level};
use transaction::{translate_tx_begin, translate_tx_commit};
use turso_sqlite3_parser::ast::{self, Delete, Insert};
use turso_sqlite3_parser::ast::{self, Delete, Indexed, Insert};
use update::translate_update;
#[instrument(skip_all, level = Level::DEBUG)]
@@ -156,16 +156,21 @@ pub fn translate_inner(
idx_name,
tbl_name,
columns,
..
} => translate_create_index(
(unique, if_not_exists),
idx_name.name.as_str(),
tbl_name.as_str(),
&columns,
schema,
syms,
program,
)?,
where_clause,
} => {
if where_clause.is_some() {
bail_parse_error!("Partial indexes are not supported");
}
translate_create_index(
(unique, if_not_exists),
idx_name.name.as_str(),
tbl_name.as_str(),
&columns,
schema,
syms,
program,
)?
}
ast::Stmt::CreateTable {
temporary,
if_not_exists,
@@ -191,8 +196,19 @@ pub fn translate_inner(
where_clause,
limit,
returning,
..
indexed,
order_by,
with,
} = *delete;
if with.is_some() {
bail_parse_error!("WITH clause is not supported in DELETE");
}
if indexed.is_some_and(|i| matches!(i, Indexed::IndexedBy(_))) {
bail_parse_error!("INDEXED BY clause is not supported in DELETE");
}
if order_by.is_some() {
bail_parse_error!("ORDER BY clause is not supported in DELETE");
}
translate_delete(
schema,
&tbl_name,

View File

@@ -46,8 +46,19 @@ pub fn resolve_aggregates(
name,
args,
distinctness,
..
filter_over,
order_by,
} => {
if filter_over.is_some() {
crate::bail_parse_error!(
"FILTER clause is not supported yet in aggregate functions"
);
}
if order_by.is_some() {
crate::bail_parse_error!(
"ORDER BY clause is not supported yet in aggregate functions"
);
}
let args_count = if let Some(args) = &args {
args.len()
} else {
@@ -84,7 +95,12 @@ pub fn resolve_aggregates(
}
}
}
Expr::FunctionCallStar { name, .. } => {
Expr::FunctionCallStar { name, filter_over } => {
if filter_over.is_some() {
crate::bail_parse_error!(
"FILTER clause is not supported yet in aggregate functions"
);
}
if let Ok(Func::Agg(f)) = Func::resolve_function(name.as_str(), 0) {
aggs.push(Aggregate {
func: f,

View File

@@ -194,6 +194,7 @@ fn prepare_one_select_plan(
where_clause,
group_by,
distinctness,
window_clause,
..
} = *select_inner;
if !schema.indexes_enabled() && distinctness.is_some() {
@@ -201,6 +202,9 @@ fn prepare_one_select_plan(
"SELECT with DISTINCT is not allowed without indexes enabled"
);
}
if window_clause.is_some() {
crate::bail_parse_error!("WINDOW clause is not supported yet");
}
let col_count = columns.len();
if col_count == 0 {
crate::bail_parse_error!("SELECT without columns is not allowed");
@@ -342,9 +346,17 @@ fn prepare_one_select_plan(
name,
distinctness,
args,
filter_over: _,
order_by: _,
filter_over,
order_by,
} => {
if filter_over.is_some() {
crate::bail_parse_error!(
"FILTER clause is not supported yet in aggregate functions"
);
}
if order_by.is_some() {
crate::bail_parse_error!("ORDER BY clause is not supported yet in aggregate functions");
}
let args_count = if let Some(args) = &args {
args.len()
} else {
@@ -465,47 +477,53 @@ fn prepare_one_select_plan(
}
}
}
ast::Expr::FunctionCallStar {
name,
filter_over: _,
} => match Func::resolve_function(name.as_str(), 0) {
Ok(Func::Agg(f)) => {
let agg = Aggregate {
func: f,
args: vec![ast::Expr::Literal(ast::Literal::Numeric(
"1".to_string(),
))],
original_expr: expr.clone(),
distinctness: Distinctness::NonDistinct,
};
aggregate_expressions.push(agg.clone());
plan.result_columns.push(ResultSetColumn {
alias: maybe_alias.as_ref().map(|alias| match alias {
ast::As::Elided(alias) => alias.as_str().to_string(),
ast::As::As(alias) => alias.as_str().to_string(),
}),
expr: expr.clone(),
contains_aggregates: true,
});
}
Ok(_) => {
ast::Expr::FunctionCallStar { name, filter_over } => {
if filter_over.is_some() {
crate::bail_parse_error!(
"Invalid aggregate function: {}",
name.as_str()
"FILTER clause is not supported yet in aggregate functions"
);
}
Err(e) => match e {
crate::LimboError::ParseError(e) => {
crate::bail_parse_error!("{}", e);
match Func::resolve_function(name.as_str(), 0) {
Ok(Func::Agg(f)) => {
let agg = Aggregate {
func: f,
args: vec![ast::Expr::Literal(ast::Literal::Numeric(
"1".to_string(),
))],
original_expr: expr.clone(),
distinctness: Distinctness::NonDistinct,
};
aggregate_expressions.push(agg.clone());
plan.result_columns.push(ResultSetColumn {
alias: maybe_alias.as_ref().map(|alias| match alias {
ast::As::Elided(alias) => {
alias.as_str().to_string()
}
ast::As::As(alias) => alias.as_str().to_string(),
}),
expr: expr.clone(),
contains_aggregates: true,
});
}
_ => {
Ok(_) => {
crate::bail_parse_error!(
"Invalid aggregate function: {}",
name.as_str()
);
}
},
},
Err(e) => match e {
crate::LimboError::ParseError(e) => {
crate::bail_parse_error!("{}", e);
}
_ => {
crate::bail_parse_error!(
"Invalid aggregate function: {}",
name.as_str()
);
}
},
}
}
expr => {
let contains_aggregates =
resolve_aggregates(schema, expr, &mut aggregate_expressions)?;

View File

@@ -12,7 +12,7 @@ use crate::{
vdbe::builder::{ProgramBuilder, ProgramBuilderOpts},
SymbolTable,
};
use turso_sqlite3_parser::ast::{Expr, SortOrder, Update};
use turso_sqlite3_parser::ast::{Expr, Indexed, SortOrder, Update};
use super::emitter::emit_program;
use super::expr::process_returning_clause;
@@ -100,10 +100,17 @@ pub fn prepare_update_plan(
connection: &Arc<crate::Connection>,
) -> crate::Result<Plan> {
if body.with.is_some() {
bail_parse_error!("WITH clause is not supported");
bail_parse_error!("WITH clause is not supported in UPDATE");
}
if body.or_conflict.is_some() {
bail_parse_error!("ON CONFLICT clause is not supported");
bail_parse_error!("ON CONFLICT clause is not supported in UPDATE");
}
if body
.indexed
.as_ref()
.is_some_and(|i| matches!(i, Indexed::IndexedBy(_)))
{
bail_parse_error!("INDEXED BY clause is not supported in UPDATE");
}
let table_name = &body.tbl_name.name;
if schema.table_has_indexes(&table_name.to_string()) && !schema.indexes_enabled() {