mirror of
				https://github.com/isar/rusqlite.git
				synced 2025-11-04 08:08:55 +08:00 
			
		
		
		
	Captured identifiers in SQL strings
Use `raw_bind_parameter`
This commit is contained in:
		@@ -12,5 +12,5 @@ categories = ["database"]
 | 
				
			|||||||
proc-macro = true
 | 
					proc-macro = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
sqlite3-parser = { version = "0.4.0", default-features = false, features = ["YYNOERRORRECOVERY"] }
 | 
					sqlite3-parser = { version = "0.5.0", default-features = false, features = ["YYNOERRORRECOVERY"] }
 | 
				
			||||||
fallible-iterator = "0.2"
 | 
					fallible-iterator = "0.2"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,8 +3,8 @@
 | 
				
			|||||||
use proc_macro::{Delimiter, Literal, TokenStream, TokenTree};
 | 
					use proc_macro::{Delimiter, Literal, TokenStream, TokenTree};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use fallible_iterator::FallibleIterator;
 | 
					use fallible_iterator::FallibleIterator;
 | 
				
			||||||
use sqlite3_parser::lexer::sql::Parser;
 | 
					 | 
				
			||||||
use sqlite3_parser::ast::{ParameterInfo, ToTokens};
 | 
					use sqlite3_parser::ast::{ParameterInfo, ToTokens};
 | 
				
			||||||
 | 
					use sqlite3_parser::lexer::sql::Parser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// https://internals.rust-lang.org/t/custom-error-diagnostics-with-procedural-macros-on-almost-stable-rust/8113
 | 
					// https://internals.rust-lang.org/t/custom-error-diagnostics-with-procedural-macros-on-almost-stable-rust/8113
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -19,7 +19,7 @@ type Result<T> = std::result::Result<T, String>;
 | 
				
			|||||||
fn try_bind(input: TokenStream) -> Result<TokenStream> {
 | 
					fn try_bind(input: TokenStream) -> Result<TokenStream> {
 | 
				
			||||||
    //eprintln!("INPUT: {:#?}", input);
 | 
					    //eprintln!("INPUT: {:#?}", input);
 | 
				
			||||||
    let (stmt, literal) = {
 | 
					    let (stmt, literal) = {
 | 
				
			||||||
        let mut iter = input.into_iter();
 | 
					        let mut iter = input.clone().into_iter();
 | 
				
			||||||
        let stmt = iter.next().unwrap();
 | 
					        let stmt = iter.next().unwrap();
 | 
				
			||||||
        let _punct = iter.next().unwrap();
 | 
					        let _punct = iter.next().unwrap();
 | 
				
			||||||
        let literal = iter.next().unwrap();
 | 
					        let literal = iter.next().unwrap();
 | 
				
			||||||
@@ -44,20 +44,35 @@ fn try_bind(input: TokenStream) -> Result<TokenStream> {
 | 
				
			|||||||
        Err(err) => {
 | 
					        Err(err) => {
 | 
				
			||||||
            return Err(err.to_string());
 | 
					            return Err(err.to_string());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Ok(Some(ast)) => ast
 | 
					        Ok(Some(ast)) => ast,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    let mut info = ParameterInfo::default();
 | 
					    let mut info = ParameterInfo::default();
 | 
				
			||||||
    if let Err(err) = ast.to_tokens(&mut info) {
 | 
					    if let Err(err) = ast.to_tokens(&mut info) {
 | 
				
			||||||
        return Err(err.to_string());
 | 
					        return Err(err.to_string());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if info.count == 0 {
 | 
				
			||||||
 | 
					        return Ok(input);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    //eprintln!("ParameterInfo.count: {:#?}", info.count);
 | 
					    //eprintln!("ParameterInfo.count: {:#?}", info.count);
 | 
				
			||||||
    //eprintln!("ParameterInfo.names: {:#?}", info.names);
 | 
					    //eprintln!("ParameterInfo.names: {:#?}", info.names);
 | 
				
			||||||
 | 
					    if info.count as usize != info.names.len() {
 | 
				
			||||||
 | 
					        return Err("Mixing named and numbered parameters is not supported.".to_string());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut res = TokenStream::new();
 | 
					    let mut res = TokenStream::new();
 | 
				
			||||||
 | 
					    for (i, name) in info.names.iter().enumerate() {
 | 
				
			||||||
 | 
					        //eprintln!("(i: {}, name: {})", i + 1, &name[1..]);
 | 
				
			||||||
 | 
					        res.extend(Some(stmt.clone()));
 | 
				
			||||||
 | 
					        res.extend(parse_ts(&format!(
 | 
				
			||||||
 | 
					            ".raw_bind_parameter({}, &{})?;",
 | 
				
			||||||
 | 
					            i + 1,
 | 
				
			||||||
 | 
					            &name[1..]
 | 
				
			||||||
 | 
					        )));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(res)
 | 
					    Ok(res)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
fn into_literal(ts: &TokenTree) -> Option<Literal> {
 | 
					fn into_literal(ts: &TokenTree) -> Option<Literal> {
 | 
				
			||||||
    match ts {
 | 
					    match ts {
 | 
				
			||||||
        TokenTree::Literal(l) => Some(l.clone()),
 | 
					        TokenTree::Literal(l) => Some(l.clone()),
 | 
				
			||||||
@@ -73,7 +88,10 @@ fn into_literal(ts: &TokenTree) -> Option<Literal> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn strip_matches<'a>(s: &'a str, pattern: &str) -> &'a str {
 | 
					fn strip_matches<'a>(s: &'a str, pattern: &str) -> &'a str {
 | 
				
			||||||
    s.strip_prefix(pattern).unwrap_or(s).strip_suffix(pattern).unwrap_or(s)
 | 
					    s.strip_prefix(pattern)
 | 
				
			||||||
 | 
					        .unwrap_or(s)
 | 
				
			||||||
 | 
					        .strip_suffix(pattern)
 | 
				
			||||||
 | 
					        .unwrap_or(s)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn parse_ts(s: &str) -> TokenStream {
 | 
					fn parse_ts(s: &str) -> TokenStream {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,23 @@
 | 
				
			|||||||
use rusqlite_macros::__bind;
 | 
					use rusqlite_macros::__bind;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Result = std::result::Result<(), String>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Stmt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Stmt {
 | 
				
			||||||
 | 
					    pub fn raw_bind_parameter(&mut self, one_based_col_index: usize, param: &str) -> Result {
 | 
				
			||||||
 | 
					        let (..) = (one_based_col_index, param);
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[test]
 | 
					#[test]
 | 
				
			||||||
fn test_literal() {
 | 
					fn test_literal() -> Result {
 | 
				
			||||||
    let stmt = ();
 | 
					    let first_name = "El";
 | 
				
			||||||
    __bind!(stmt, "SELECT $name");
 | 
					    let last_name = "Barto";
 | 
				
			||||||
 | 
					    let mut stmt = Stmt;
 | 
				
			||||||
 | 
					    __bind!(stmt, "SELECT $first_name, $last_name");
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* FIXME
 | 
					/* FIXME
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user