mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-19 08:04:19 +01:00
vendor sqlite3-parser (lemon-rs)
This commit is contained in:
336
vendored/sqlite3-parser/src/parser/ast/check.rs
Normal file
336
vendored/sqlite3-parser/src/parser/ast/check.rs
Normal file
@@ -0,0 +1,336 @@
|
||||
//! Check for additional syntax error
|
||||
use crate::ast::*;
|
||||
use crate::custom_err;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
impl Cmd {
|
||||
/// Statement accessor
|
||||
pub fn stmt(&self) -> &Stmt {
|
||||
match self {
|
||||
Self::Explain(stmt) => stmt,
|
||||
Self::ExplainQueryPlan(stmt) => stmt,
|
||||
Self::Stmt(stmt) => stmt,
|
||||
}
|
||||
}
|
||||
/// Like `sqlite3_column_count` but more limited
|
||||
pub fn column_count(&self) -> ColumnCount {
|
||||
match self {
|
||||
Self::Explain(_) => ColumnCount::Fixed(8),
|
||||
Self::ExplainQueryPlan(_) => ColumnCount::Fixed(4),
|
||||
Self::Stmt(stmt) => stmt.column_count(),
|
||||
}
|
||||
}
|
||||
/// Like `sqlite3_stmt_isexplain`
|
||||
pub fn is_explain(&self) -> bool {
|
||||
matches!(self, Self::Explain(_) | Self::ExplainQueryPlan(_))
|
||||
}
|
||||
/// Like `sqlite3_stmt_readonly`
|
||||
pub fn readonly(&self) -> bool {
|
||||
self.stmt().readonly()
|
||||
}
|
||||
/// check for extra rules
|
||||
pub fn check(&self) -> Result<(), ParserError> {
|
||||
self.stmt().check()
|
||||
}
|
||||
}
|
||||
|
||||
/// Column count
|
||||
pub enum ColumnCount {
|
||||
/// With `SELECT *` / PRAGMA
|
||||
Dynamic,
|
||||
/// Constant count
|
||||
Fixed(usize),
|
||||
/// No column
|
||||
None,
|
||||
}
|
||||
|
||||
impl ColumnCount {
|
||||
fn incr(&mut self) {
|
||||
if let Self::Fixed(n) = self {
|
||||
*n += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Stmt {
|
||||
/// Like `sqlite3_column_count` but more limited
|
||||
pub fn column_count(&self) -> ColumnCount {
|
||||
match self {
|
||||
Self::Delete {
|
||||
returning: Some(returning),
|
||||
..
|
||||
} => column_count(returning),
|
||||
Self::Insert {
|
||||
returning: Some(returning),
|
||||
..
|
||||
} => column_count(returning),
|
||||
Self::Pragma(..) => ColumnCount::Dynamic,
|
||||
Self::Select(s) => s.column_count(),
|
||||
Self::Update {
|
||||
returning: Some(returning),
|
||||
..
|
||||
} => column_count(returning),
|
||||
_ => ColumnCount::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Like `sqlite3_stmt_readonly`
|
||||
pub fn readonly(&self) -> bool {
|
||||
match self {
|
||||
Self::Attach { .. } => true,
|
||||
Self::Begin(..) => true,
|
||||
Self::Commit(..) => true,
|
||||
Self::Detach(..) => true,
|
||||
Self::Pragma(..) => true, // TODO check all
|
||||
Self::Reindex { .. } => true,
|
||||
Self::Release(..) => true,
|
||||
Self::Rollback { .. } => true,
|
||||
Self::Savepoint(..) => true,
|
||||
Self::Select(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// check for extra rules
|
||||
pub fn check(&self) -> Result<(), ParserError> {
|
||||
match self {
|
||||
Self::AlterTable(old_name, AlterTableBody::RenameTo(new_name)) => {
|
||||
if *new_name == old_name.name {
|
||||
return Err(custom_err!(
|
||||
"there is already another table or index with this name: {}",
|
||||
new_name
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Self::AlterTable(.., AlterTableBody::AddColumn(cd)) => {
|
||||
for c in cd {
|
||||
if let ColumnConstraint::PrimaryKey { .. } = c {
|
||||
return Err(custom_err!("Cannot add a PRIMARY KEY column"));
|
||||
} else if let ColumnConstraint::Unique(..) = c {
|
||||
return Err(custom_err!("Cannot add a UNIQUE column"));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Self::CreateTable {
|
||||
temporary,
|
||||
tbl_name,
|
||||
body,
|
||||
..
|
||||
} => {
|
||||
if *temporary {
|
||||
if let Some(ref db_name) = tbl_name.db_name {
|
||||
if db_name != "TEMP" {
|
||||
return Err(custom_err!("temporary table name must be unqualified"));
|
||||
}
|
||||
}
|
||||
}
|
||||
body.check(tbl_name)
|
||||
}
|
||||
Self::CreateView {
|
||||
view_name,
|
||||
columns: Some(columns),
|
||||
select,
|
||||
..
|
||||
} => {
|
||||
// SQLite3 engine renames duplicates:
|
||||
for (i, c) in columns.iter().enumerate() {
|
||||
for o in &columns[i + 1..] {
|
||||
if c.col_name == o.col_name {
|
||||
return Err(custom_err!("duplicate column name: {}", c.col_name,));
|
||||
}
|
||||
}
|
||||
}
|
||||
// SQLite3 engine raises this error later (not while parsing):
|
||||
match select.column_count() {
|
||||
ColumnCount::Fixed(n) if n != columns.len() => Err(custom_err!(
|
||||
"expected {} columns for {} but got {}",
|
||||
columns.len(),
|
||||
view_name,
|
||||
n
|
||||
)),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
Self::Delete {
|
||||
order_by: Some(_),
|
||||
limit: None,
|
||||
..
|
||||
} => Err(custom_err!("ORDER BY without LIMIT on DELETE")),
|
||||
Self::Insert {
|
||||
columns: Some(columns),
|
||||
body: InsertBody::Select(select, ..),
|
||||
..
|
||||
} => match select.body.select.column_count() {
|
||||
ColumnCount::Fixed(n) if n != columns.len() => {
|
||||
Err(custom_err!("{} values for {} columns", n, columns.len()))
|
||||
}
|
||||
_ => Ok(()),
|
||||
},
|
||||
Self::Insert {
|
||||
columns: Some(columns),
|
||||
body: InsertBody::DefaultValues,
|
||||
..
|
||||
} => Err(custom_err!("0 values for {} columns", columns.len())),
|
||||
Self::Update {
|
||||
order_by: Some(_),
|
||||
limit: None,
|
||||
..
|
||||
} => Err(custom_err!("ORDER BY without LIMIT on UPDATE")),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CreateTableBody {
|
||||
/// check for extra rules
|
||||
pub fn check(&self, tbl_name: &QualifiedName) -> Result<(), ParserError> {
|
||||
if let Self::ColumnsAndConstraints {
|
||||
columns,
|
||||
constraints: _,
|
||||
options,
|
||||
} = self
|
||||
{
|
||||
let mut generated_count = 0;
|
||||
for c in columns.values() {
|
||||
for cs in &c.constraints {
|
||||
if let ColumnConstraint::Generated { .. } = cs.constraint {
|
||||
generated_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if generated_count == columns.len() {
|
||||
return Err(custom_err!("must have at least one non-generated column"));
|
||||
}
|
||||
|
||||
if options.contains(TableOptions::STRICT) {
|
||||
for c in columns.values() {
|
||||
match &c.col_type {
|
||||
Some(Type { name, .. }) => {
|
||||
// The datatype must be one of following: INT INTEGER REAL TEXT BLOB ANY
|
||||
if !(name.eq_ignore_ascii_case("INT")
|
||||
|| name.eq_ignore_ascii_case("INTEGER")
|
||||
|| name.eq_ignore_ascii_case("REAL")
|
||||
|| name.eq_ignore_ascii_case("TEXT")
|
||||
|| name.eq_ignore_ascii_case("BLOB")
|
||||
|| name.eq_ignore_ascii_case("ANY"))
|
||||
{
|
||||
return Err(custom_err!(
|
||||
"unknown datatype for {}.{}: \"{}\"",
|
||||
tbl_name,
|
||||
c.col_name,
|
||||
name
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Every column definition must specify a datatype for that column. The freedom to specify a column without a datatype is removed.
|
||||
return Err(custom_err!(
|
||||
"missing datatype for {}.{}",
|
||||
tbl_name,
|
||||
c.col_name
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if options.contains(TableOptions::WITHOUT_ROWID) && !self.has_primary_key() {
|
||||
return Err(custom_err!("PRIMARY KEY missing on table {}", tbl_name,));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// explicit primary key constraint ?
|
||||
pub fn has_primary_key(&self) -> bool {
|
||||
if let Self::ColumnsAndConstraints {
|
||||
columns,
|
||||
constraints,
|
||||
..
|
||||
} = self
|
||||
{
|
||||
for col in columns.values() {
|
||||
for c in col {
|
||||
if let ColumnConstraint::PrimaryKey { .. } = c {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(constraints) = constraints {
|
||||
for c in constraints {
|
||||
if let TableConstraint::PrimaryKey { .. } = c.constraint {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a ColumnDefinition {
|
||||
type Item = &'a ColumnConstraint;
|
||||
type IntoIter = std::iter::Map<
|
||||
std::slice::Iter<'a, NamedColumnConstraint>,
|
||||
fn(&'a NamedColumnConstraint) -> &'a ColumnConstraint,
|
||||
>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.constraints.iter().map(|nc| &nc.constraint)
|
||||
}
|
||||
}
|
||||
|
||||
impl Select {
|
||||
/// Like `sqlite3_column_count` but more limited
|
||||
pub fn column_count(&self) -> ColumnCount {
|
||||
self.body.select.column_count()
|
||||
}
|
||||
}
|
||||
|
||||
impl OneSelect {
|
||||
/// Like `sqlite3_column_count` but more limited
|
||||
pub fn column_count(&self) -> ColumnCount {
|
||||
match self {
|
||||
Self::Select { columns, .. } => column_count(columns),
|
||||
Self::Values(values) => {
|
||||
assert!(!values.is_empty()); // TODO Validate
|
||||
ColumnCount::Fixed(values[0].len())
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Check all VALUES have the same number of terms
|
||||
pub fn push(values: &mut Vec<Vec<Expr>>, v: Vec<Expr>) -> Result<(), ParserError> {
|
||||
if values[0].len() != v.len() {
|
||||
return Err(custom_err!("all VALUES must have the same number of terms"));
|
||||
}
|
||||
values.push(v);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for QualifiedName {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
self.to_fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ResultColumn {
|
||||
fn column_count(&self) -> ColumnCount {
|
||||
match self {
|
||||
Self::Expr(..) => ColumnCount::Fixed(1),
|
||||
_ => ColumnCount::Dynamic,
|
||||
}
|
||||
}
|
||||
}
|
||||
fn column_count(cols: &[ResultColumn]) -> ColumnCount {
|
||||
assert!(!cols.is_empty());
|
||||
let mut count = ColumnCount::Fixed(0);
|
||||
for col in cols {
|
||||
match col.column_count() {
|
||||
ColumnCount::Fixed(_) => count.incr(),
|
||||
_ => return ColumnCount::Dynamic,
|
||||
}
|
||||
}
|
||||
count
|
||||
}
|
||||
2037
vendored/sqlite3-parser/src/parser/ast/fmt.rs
Normal file
2037
vendored/sqlite3-parser/src/parser/ast/fmt.rs
Normal file
File diff suppressed because it is too large
Load Diff
1860
vendored/sqlite3-parser/src/parser/ast/mod.rs
Normal file
1860
vendored/sqlite3-parser/src/parser/ast/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
154
vendored/sqlite3-parser/src/parser/mod.rs
Normal file
154
vendored/sqlite3-parser/src/parser/mod.rs
Normal file
@@ -0,0 +1,154 @@
|
||||
//! SQLite parser
|
||||
|
||||
pub mod ast;
|
||||
pub mod parse {
|
||||
#![expect(unused_braces)]
|
||||
#![expect(clippy::if_same_then_else)]
|
||||
#![expect(clippy::absurd_extreme_comparisons)] // FIXME
|
||||
#![expect(clippy::needless_return)]
|
||||
#![expect(clippy::upper_case_acronyms)]
|
||||
#![expect(clippy::manual_range_patterns)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/parse.rs"));
|
||||
}
|
||||
|
||||
use crate::dialect::Token;
|
||||
use ast::{Cmd, ExplainKind, Name, Stmt};
|
||||
|
||||
/// Parser error
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ParserError {
|
||||
/// Syntax error
|
||||
SyntaxError(String),
|
||||
/// Unexpected EOF
|
||||
UnexpectedEof,
|
||||
/// Custom error
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ParserError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::SyntaxError(s) => {
|
||||
write!(f, "near \"{s}\": syntax error")
|
||||
}
|
||||
Self::UnexpectedEof => f.write_str("unexpected end of input"),
|
||||
Self::Custom(s) => f.write_str(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParserError {}
|
||||
|
||||
/// Custom error constructor
|
||||
#[macro_export]
|
||||
macro_rules! custom_err {
|
||||
($msg:literal $(,)?) => {
|
||||
$crate::parser::ParserError::Custom($msg.to_owned())
|
||||
};
|
||||
($err:expr $(,)?) => {
|
||||
$crate::parser::ParserError::Custom(format!($err))
|
||||
};
|
||||
($fmt:expr, $($arg:tt)*) => {
|
||||
$crate::parser::ParserError::Custom(format!($fmt, $($arg)*))
|
||||
};
|
||||
}
|
||||
|
||||
/// Parser context
|
||||
pub struct Context<'input> {
|
||||
input: &'input [u8],
|
||||
explain: Option<ExplainKind>,
|
||||
stmt: Option<Stmt>,
|
||||
constraint_name: Option<Name>, // transient
|
||||
module_arg: Option<(usize, usize)>, // Complete text of a module argument
|
||||
module_args: Option<Vec<String>>, // CREATE VIRTUAL TABLE args
|
||||
done: bool,
|
||||
error: Option<ParserError>,
|
||||
}
|
||||
|
||||
impl<'input> Context<'input> {
|
||||
pub fn new(input: &'input [u8]) -> Self {
|
||||
Context {
|
||||
input,
|
||||
explain: None,
|
||||
stmt: None,
|
||||
constraint_name: None,
|
||||
module_arg: None,
|
||||
module_args: None,
|
||||
done: false,
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume parsed command
|
||||
pub fn cmd(&mut self) -> Option<Cmd> {
|
||||
if let Some(stmt) = self.stmt.take() {
|
||||
match self.explain.take() {
|
||||
Some(ExplainKind::Explain) => Some(Cmd::Explain(stmt)),
|
||||
Some(ExplainKind::QueryPlan) => Some(Cmd::ExplainQueryPlan(stmt)),
|
||||
None => Some(Cmd::Stmt(stmt)),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn constraint_name(&mut self) -> Option<Name> {
|
||||
self.constraint_name.take()
|
||||
}
|
||||
fn no_constraint_name(&self) -> bool {
|
||||
self.constraint_name.is_none()
|
||||
}
|
||||
|
||||
fn vtab_arg_init(&mut self) {
|
||||
self.add_module_arg();
|
||||
self.module_arg = None;
|
||||
}
|
||||
fn vtab_arg_extend(&mut self, any: Token) {
|
||||
if let Some((_, ref mut n)) = self.module_arg {
|
||||
*n = any.2
|
||||
} else {
|
||||
self.module_arg = Some((any.0, any.2))
|
||||
}
|
||||
}
|
||||
fn add_module_arg(&mut self) {
|
||||
if let Some((start, end)) = self.module_arg.take() {
|
||||
if let Ok(arg) = std::str::from_utf8(&self.input[start..end]) {
|
||||
self.module_args.get_or_insert(vec![]).push(arg.to_owned());
|
||||
} // FIXME error handling
|
||||
}
|
||||
}
|
||||
fn module_args(&mut self) -> Option<Vec<String>> {
|
||||
self.add_module_arg();
|
||||
self.module_args.take()
|
||||
}
|
||||
|
||||
/// This routine is called after a single SQL statement has been parsed.
|
||||
fn sqlite3_finish_coding(&mut self) {
|
||||
self.done = true;
|
||||
}
|
||||
|
||||
/// Return `true` if parser completes either successfully or with an error.
|
||||
pub fn done(&self) -> bool {
|
||||
self.done || self.error.is_some()
|
||||
}
|
||||
|
||||
pub fn is_ok(&self) -> bool {
|
||||
self.error.is_none()
|
||||
}
|
||||
|
||||
/// Consume error generated by parser
|
||||
pub fn error(&mut self) -> Option<ParserError> {
|
||||
self.error.take()
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.explain = None;
|
||||
self.stmt = None;
|
||||
self.constraint_name = None;
|
||||
self.module_arg = None;
|
||||
self.module_args = None;
|
||||
self.done = false;
|
||||
self.error = None;
|
||||
}
|
||||
}
|
||||
1492
vendored/sqlite3-parser/src/parser/parse.y
Normal file
1492
vendored/sqlite3-parser/src/parser/parse.y
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user