Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions datafusion-cli/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::helper::split_from_semicolon;
use crate::print_format::PrintFormat;
use crate::{
command::{Command, OutputFormat},
helper::{unescape_input, CliHelper},
helper::CliHelper,
object_storage::get_object_store,
print_options::{MaxRows, PrintOptions},
};
Expand Down Expand Up @@ -172,7 +172,7 @@ pub async fn exec_from_repl(
}
}
Ok(line) => {
let lines = split_from_semicolon(line);
let lines = split_from_semicolon(&line);
for line in lines {
rl.add_history_entry(line.trim_end())?;
tokio::select! {
Expand Down Expand Up @@ -215,7 +215,6 @@ pub(super) async fn exec_and_print(
sql: String,
) -> Result<()> {
let now = Instant::now();
let sql = unescape_input(&sql)?;
let task_ctx = ctx.task_ctx();
let dialect = &task_ctx.session_config().options().sql_parser.dialect;
let dialect = dialect_from_str(dialect).ok_or_else(|| {
Expand Down
76 changes: 8 additions & 68 deletions datafusion-cli/src/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ use std::borrow::Cow;

use crate::highlighter::{NoSyntaxHighlighter, SyntaxHighlighter};

use datafusion::common::sql_datafusion_err;
use datafusion::error::DataFusionError;
use datafusion::sql::parser::{DFParser, Statement};
use datafusion::sql::sqlparser::dialect::dialect_from_str;
use datafusion::sql::sqlparser::parser::ParserError;

use rustyline::completion::{Completer, FilenameCompleter, Pair};
use rustyline::error::ReadlineError;
Expand Down Expand Up @@ -63,15 +60,6 @@ impl CliHelper {

fn validate_input(&self, input: &str) -> Result<ValidationResult> {
if let Some(sql) = input.strip_suffix(';') {
let sql = match unescape_input(sql) {
Ok(sql) => sql,
Err(err) => {
return Ok(ValidationResult::Invalid(Some(format!(
" 🤔 Invalid statement: {err}",
))))
}
};

let dialect = match dialect_from_str(&self.dialect) {
Some(dialect) => dialect,
None => {
Expand Down Expand Up @@ -166,56 +154,8 @@ impl Validator for CliHelper {

impl Helper for CliHelper {}

/// Unescape input string from readline.
///
/// The data read from stdio will be escaped, so we need to unescape the input before executing the input
pub fn unescape_input(input: &str) -> datafusion::error::Result<String> {
let mut chars = input.chars().peekable();
let mut result = String::with_capacity(input.len());

while let Some(ch) = chars.next() {
if ch == '\\' {
if let Some(&next) = chars.peek() {
match next {
'0' => {
chars.next();
result.push('\0');
}
'n' => {
chars.next();
result.push('\n');
}
'r' => {
chars.next();
result.push('\r');
}
't' => {
chars.next();
result.push('\t');
}
'\\' | '\'' => result.push('\\'),
_ => {
return Err(DataFusionError::Execution(format!(
"Invalid escape sequence: \\{}",
next
)))
}
}
} else {
return Err(sql_datafusion_err!(ParserError::TokenizerError(
"incomplete escape sequence: trailing backslash".to_string()
)));
}
} else {
result.push(ch);
}
}

Ok(result)
}

/// Splits a string which consists of multiple queries.
pub(crate) fn split_from_semicolon(sql: String) -> Vec<String> {
pub(crate) fn split_from_semicolon(sql: &str) -> Vec<String> {
let mut commands = Vec::new();
let mut current_command = String::new();
let mut in_single_quote = false;
Expand Down Expand Up @@ -370,34 +310,34 @@ mod tests {
fn test_split_from_semicolon() {
let sql = "SELECT 1; SELECT 2;";
let expected = vec!["SELECT 1;", "SELECT 2;"];
assert_eq!(split_from_semicolon(sql.to_string()), expected);
assert_eq!(split_from_semicolon(sql), expected);

let sql = r#"SELECT ";";"#;
let expected = vec![r#"SELECT ";";"#];
assert_eq!(split_from_semicolon(sql.to_string()), expected);
assert_eq!(split_from_semicolon(sql), expected);

let sql = "SELECT ';';";
let expected = vec!["SELECT ';';"];
assert_eq!(split_from_semicolon(sql.to_string()), expected);
assert_eq!(split_from_semicolon(sql), expected);

let sql = r#"SELECT 1; SELECT 'value;value'; SELECT 1 as "text;text";"#;
let expected = vec![
"SELECT 1;",
"SELECT 'value;value';",
r#"SELECT 1 as "text;text";"#,
];
assert_eq!(split_from_semicolon(sql.to_string()), expected);
assert_eq!(split_from_semicolon(sql), expected);

let sql = "";
let expected: Vec<String> = Vec::new();
assert_eq!(split_from_semicolon(sql.to_string()), expected);
assert_eq!(split_from_semicolon(sql), expected);

let sql = "SELECT 1";
let expected = vec!["SELECT 1;"];
assert_eq!(split_from_semicolon(sql.to_string()), expected);
assert_eq!(split_from_semicolon(sql), expected);

let sql = "SELECT 1; ";
let expected = vec!["SELECT 1;"];
assert_eq!(split_from_semicolon(sql.to_string()), expected);
assert_eq!(split_from_semicolon(sql), expected);
}
}