use crate::diagnostic::{ParseError, ParseErrorKind};
use crate::lexer::{QuoteForm, Span};
pub(crate) fn schema_refused(message: impl Into<String>, span: Span) -> ParseError {
ParseError::new(ParseErrorKind::Refused(message.into()), span)
}
pub(crate) fn unsupported(what: &'static str, span: Span) -> ParseError {
ParseError::new(ParseErrorKind::Unsupported(what), span)
}
pub(crate) fn no_such_table(name: &[u8], span: Span) -> ParseError {
let _ = span;
ParseError::new(
ParseErrorKind::Refused(format!("no such table: {}", String::from_utf8_lossy(name))),
Span::default(),
)
}
pub(crate) fn qualify_missing_table(error: ParseError, database: &[u8]) -> ParseError {
let ParseErrorKind::Refused(message) = &error.kind else {
return error;
};
let Some(missing) = message.strip_prefix("no such table: ") else {
return error;
};
if missing.contains('.') || database.eq_ignore_ascii_case(b"temp") {
return error;
}
let qualified = format!(
"no such table: {}.{missing}",
String::from_utf8_lossy(database)
);
ParseError::new(ParseErrorKind::Refused(qualified), error.span)
}
pub(crate) fn no_such_column_quoted(name: &[u8], quote: QuoteForm, span: Span) -> ParseError {
if quote != QuoteForm::Double {
return no_such_column(name, span);
}
ParseError::new(
ParseErrorKind::Refused(format!(
"no such column: \"{}\" - should this be a string literal in single-quotes?",
String::from_utf8_lossy(name)
)),
span,
)
}
pub(crate) fn no_such_index(name: &[u8], span: Span) -> ParseError {
let _ = span;
ParseError::new(
ParseErrorKind::Refused(format!("no such index: {}", String::from_utf8_lossy(name))),
Span::default(),
)
}
pub(crate) fn no_query_solution(span: Span) -> ParseError {
ParseError::new(
ParseErrorKind::Refused("no query solution".to_string()),
span,
)
}
pub(crate) fn no_such_column(name: &[u8], span: Span) -> ParseError {
ParseError::new(
ParseErrorKind::Refused(format!("no such column: {}", String::from_utf8_lossy(name))),
span,
)
}
pub(crate) fn ambiguous_column(name: &[u8], span: Span) -> ParseError {
ParseError::new(
ParseErrorKind::Refused(format!(
"ambiguous column name: {}",
String::from_utf8_lossy(name)
)),
span,
)
}
const WINDOW_ONLY: &[&[u8]] = &[
b"cume_dist",
b"dense_rank",
b"first_value",
b"lag",
b"last_value",
b"lead",
b"nth_value",
b"ntile",
b"percent_rank",
b"rank",
b"row_number",
];
const CONTEXT_ONLY: &[&[u8]] = &[
b"bm25",
b"fts5",
b"fts5_get_locale",
b"fts5_insttoken",
b"fts5_locale",
b"highlight",
b"match",
b"matchinfo",
b"offsets",
b"optimize",
b"snippet",
];
pub(crate) fn no_such_function(written: &[u8], span: Span) -> ParseError {
let folded = written.to_ascii_lowercase();
let name = folded.as_slice();
if let Some(said) = crate::function::needs_a_component(name) {
return ParseError::new(ParseErrorKind::Unsupported(said), span);
}
if WINDOW_ONLY.contains(&name) {
return ParseError::new(
ParseErrorKind::Refused(format!(
"misuse of window function {}()",
String::from_utf8_lossy(name)
)),
span,
);
}
if CONTEXT_ONLY.contains(&name) {
let spelled = if name == b"match" {
"MATCH".to_string()
} else {
String::from_utf8_lossy(written).into_owned()
};
return ParseError::new(
ParseErrorKind::Refused(format!(
"unable to use function {spelled} in the requested context"
)),
span,
);
}
ParseError::new(
ParseErrorKind::Refused(format!(
"no such function: {}",
String::from_utf8_lossy(written)
)),
span,
)
}
pub(crate) fn wrong_arguments(name: &[u8], span: Span) -> ParseError {
ParseError::new(
ParseErrorKind::Refused(format!(
"wrong number of arguments to function {}()",
String::from_utf8_lossy(name)
)),
span,
)
}
pub(crate) fn no_such_collation(name: &[u8], span: Span) -> ParseError {
let _ = span;
ParseError::new(
ParseErrorKind::Refused(format!(
"no such collation sequence: {}",
String::from_utf8_lossy(name)
)),
Span::default(),
)
}
pub(crate) fn ordinal(number: usize) -> String {
let suffix = match (number % 100, number % 10) {
(11..=13, _) => "th",
(_, 1) => "st",
(_, 2) => "nd",
(_, 3) => "rd",
_ => "th",
};
format!("{number}{suffix}")
}
pub(crate) fn ordinal_word(position: usize) -> String {
let suffix = match (position % 100, position % 10) {
(11..=13, _) => "th",
(_, 1) => "st",
(_, 2) => "nd",
(_, 3) => "rd",
_ => "th",
};
format!("{position}{suffix}")
}
pub(crate) fn order_out_of_range(position: usize, columns: usize, span: Span) -> ParseError {
ParseError::new(
ParseErrorKind::Refused(format!(
"{} ORDER BY term out of range - should be between 1 and {columns}",
ordinal_word(position)
)),
span,
)
}
pub(crate) fn group_out_of_range(position: usize, columns: usize, span: Span) -> ParseError {
ParseError::new(
ParseErrorKind::Refused(format!(
"{} GROUP BY term out of range - should be between 1 and {columns}",
ordinal_word(position)
)),
span,
)
}
pub(crate) fn compound_order_unmatched(position: usize, span: Span) -> ParseError {
let _ = span;
ParseError::new(
ParseErrorKind::Refused(format!(
"{} ORDER BY term does not match any column in the result set",
ordinal(position)
)),
Span::default(),
)
}
pub(crate) fn compound_width_mismatch(operator: crate::ast::CompoundOp, span: Span) -> ParseError {
let _ = span;
let name = match operator {
crate::ast::CompoundOp::Union => "UNION",
crate::ast::CompoundOp::UnionAll => "UNION ALL",
crate::ast::CompoundOp::Intersect => "INTERSECT",
crate::ast::CompoundOp::Except => "EXCEPT",
};
ParseError::new(
ParseErrorKind::Refused(format!(
"SELECTs to the left and right of {name} do not have the same number of result columns"
)),
Span::default(),
)
}
pub(crate) fn values_width_mismatch(span: Span) -> ParseError {
let _ = span;
ParseError::new(
ParseErrorKind::Refused("all VALUES must have the same number of terms".to_string()),
Span::default(),
)
}
pub(crate) fn reword_for_aggregate_query(error: ParseError) -> ParseError {
match &error.kind {
ParseErrorKind::Refused(message)
if message.starts_with("misuse of aggregate function ") =>
{
let reworded = message.replacen("aggregate function ", "aggregate: ", 1);
ParseError::new(ParseErrorKind::Refused(reworded), error.span)
}
_ => error,
}
}
pub(crate) fn named_column_count(
name: &[u8],
found: usize,
named: usize,
span: Span,
) -> ParseError {
let _ = span;
ParseError::new(
ParseErrorKind::Refused(format!(
"table {} has {found} values for {named} columns",
String::from_utf8_lossy(name)
)),
Span::default(),
)
}
pub(crate) fn subquery_width_mismatch(found: usize, expected: usize, span: Span) -> ParseError {
let _ = span;
ParseError::new(
ParseErrorKind::Refused(format!(
"sub-select returns {found} columns - expected {expected}"
)),
Span::default(),
)
}