libsql-sqlite3-parser 0.13.0

SQL parser (as understood by SQLite) (libsql fork)
Documentation
use fallible_iterator::FallibleIterator;

use super::{Error, Parser};
use crate::parser::{
    ast::{Cmd, Name, ParameterInfo, QualifiedName, Stmt, ToTokens},
    ParserError,
};

#[test]
fn count_placeholders() -> Result<(), Error> {
    let sql = "SELECT ? WHERE 1 = ?";
    let mut parser = Parser::new(sql.as_bytes());
    let ast = parser.next()?.unwrap();
    let mut info = ParameterInfo::default();
    ast.to_tokens(&mut info).unwrap();
    assert_eq!(info.count, 2);
    Ok(())
}

#[test]
fn count_numbered_placeholders() -> Result<(), Error> {
    let sql = "SELECT ?1 WHERE 1 = ?2 AND 0 = ?1";
    let mut parser = Parser::new(sql.as_bytes());
    let ast = parser.next()?.unwrap();
    let mut info = ParameterInfo::default();
    ast.to_tokens(&mut info).unwrap();
    assert_eq!(info.count, 2);
    Ok(())
}

#[test]
fn count_unused_placeholders() -> Result<(), Error> {
    let sql = "SELECT ?1 WHERE 1 = ?3";
    let mut parser = Parser::new(sql.as_bytes());
    let ast = parser.next()?.unwrap();
    let mut info = ParameterInfo::default();
    ast.to_tokens(&mut info).unwrap();
    assert_eq!(info.count, 3);
    Ok(())
}

#[test]
fn count_named_placeholders() -> Result<(), Error> {
    let sql = "SELECT :x, :y WHERE 1 = :y";
    let mut parser = Parser::new(sql.as_bytes());
    let ast = parser.next()?.unwrap();
    let mut info = ParameterInfo::default();
    ast.to_tokens(&mut info).unwrap();
    assert_eq!(info.count, 2);
    assert_eq!(info.names.len(), 2);
    assert!(info.names.contains(":x"));
    assert!(info.names.contains(":y"));
    Ok(())
}

#[test]
fn duplicate_column() {
    let sql = "CREATE TABLE t (x TEXT, x TEXT)";
    let mut parser = Parser::new(sql.as_bytes());
    let r = parser.next();
    let Error::ParserError(ParserError::Custom(msg), _) = r.unwrap_err() else {
        panic!("unexpected error type")
    };
    assert!(msg.contains("duplicate column name"));
}

#[test]
fn vtab_args() -> Result<(), Error> {
    let sql = r#"CREATE VIRTUAL TABLE mail USING fts3(
  subject VARCHAR(256) NOT NULL,
  body TEXT CHECK(length(body)<10240)
);"#;
    let mut parser = Parser::new(sql.as_bytes());
    let Cmd::Stmt(Stmt::CreateVirtualTable {
        tbl_name: QualifiedName {
            name: Name(tbl_name),
            ..
        },
        module_name: Name(module_name),
        args: Some(args),
        ..
    }) = parser.next()?.unwrap()
    else {
        panic!("unexpected AST")
    };
    assert_eq!(tbl_name, "mail");
    assert_eq!(module_name, "fts3");
    assert_eq!(args.len(), 2);
    assert_eq!(args[0], "subject VARCHAR(256) NOT NULL");
    assert_eq!(args[1], "body TEXT CHECK(length(body)<10240)");
    Ok(())
}

#[test]
fn only_semicolons_no_statements() {
    let sqls = ["", ";", ";;;"];
    for sql in sqls.iter() {
        let mut parser = Parser::new(sql.as_bytes());
        assert_eq!(parser.next().unwrap(), None);
    }
}

#[test]
fn extra_semicolons_between_statements() {
    let sqls = [
        "SELECT 1; SELECT 2",
        "SELECT 1; SELECT 2;",
        "; SELECT 1; SELECT 2",
        ";; SELECT 1;; SELECT 2;;",
    ];
    for sql in sqls.iter() {
        let mut parser = Parser::new(sql.as_bytes());
        assert!(matches!(
            parser.next().unwrap(),
            Some(Cmd::Stmt(Stmt::Select { .. }))
        ));
        assert!(matches!(
            parser.next().unwrap(),
            Some(Cmd::Stmt(Stmt::Select { .. }))
        ));
        assert_eq!(parser.next().unwrap(), None);
    }
}