use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::time::Instant;
use datafusion::common::Result;
use datafusion::prelude::SessionContext;
use rustyline::Editor;
use rustyline::error::ReadlineError;
use crate::{
command::{Command, OutputFormat},
helper::CliHelper,
print_options::PrintOptions,
};
pub async fn exec_from_lines(
ctx: &SessionContext,
reader: &mut BufReader<File>,
print_options: &PrintOptions,
) {
let mut query = "".to_owned();
for line in reader.lines() {
match line {
Ok(line) if line.starts_with("--") => {
continue;
}
Ok(line) => {
let line = line.trim_end();
query.push_str(line);
if line.ends_with(';') {
match exec_and_print(ctx, print_options, query).await {
Ok(_) => {}
Err(err) => println!("{err:?}"),
}
#[allow(clippy::assigning_clones)]
{
query = "".to_owned();
}
} else {
query.push('\n');
}
}
_ => {
break;
}
}
}
if !query.is_empty() {
match exec_and_print(ctx, print_options, query).await {
Ok(_) => {}
Err(err) => println!("{err:?}"),
}
}
}
pub async fn exec_from_files(
files: Vec<String>,
ctx: &SessionContext,
print_options: &PrintOptions,
) {
let files = files
.into_iter()
.map(|file_path| File::open(file_path).unwrap())
.collect::<Vec<_>>();
for file in files {
let mut reader = BufReader::new(file);
exec_from_lines(ctx, &mut reader, print_options).await;
}
}
pub async fn exec_from_repl(
ctx: &SessionContext,
print_options: &mut PrintOptions,
tui_mode: Arc<AtomicBool>,
) {
let mut rl = Editor::new().expect("created editor");
rl.set_helper(Some(CliHelper::new(
&ctx.task_ctx().session_config().options().sql_parser.dialect,
print_options.color,
)));
rl.load_history(".history").ok();
let mut print_options = print_options.clone();
loop {
match rl.readline("❯ ") {
Ok(line) if line.starts_with('\\') => {
rl.add_history_entry(line.trim_end()).unwrap();
let command = line.split_whitespace().collect::<Vec<_>>().join(" ");
if let Ok(cmd) = &command[1..].parse::<Command>() {
match cmd {
Command::Quit => break,
Command::OutputFormat(subcommand) => {
if let Some(subcommand) = subcommand {
if let Ok(command) = subcommand.parse::<OutputFormat>() {
if let Err(e) =
command.execute(&mut print_options).await
{
eprintln!("{e}")
}
} else {
eprintln!(
"'\\{}' is not a valid command",
&line[1..]
);
}
} else {
println!("Output format is {:?}.", print_options.format);
}
}
_ => {
if let Err(e) = cmd
.execute(ctx, &mut print_options, tui_mode.clone())
.await
{
eprintln!("{e}")
}
}
}
} else {
eprintln!("'\\{}' is not a valid command", &line[1..]);
}
}
Ok(line) => {
rl.add_history_entry(line.trim_end()).unwrap();
match exec_and_print(ctx, &print_options, line).await {
Ok(_) => {}
Err(err) => eprintln!("{err:?}"),
}
}
Err(ReadlineError::Interrupted) => {
println!("^C");
continue;
}
Err(ReadlineError::Eof) => {
println!("\\q");
break;
}
Err(err) => {
eprintln!("Unknown error happened {err:?}");
break;
}
}
}
rl.save_history(".history").ok();
}
async fn exec_and_print(
ctx: &SessionContext,
print_options: &PrintOptions,
sql: String,
) -> Result<()> {
let now = Instant::now();
let df = ctx.sql(&sql).await?;
let df = match print_options.maxrows {
datafusion_cli::print_options::MaxRows::Unlimited => df,
datafusion_cli::print_options::MaxRows::Limited(max_rows) => {
df.limit(0, Some(max_rows))?
}
};
let schema = Arc::new(df.schema().as_arrow().clone());
let results = df.collect().await?;
let rows = &results.iter().map(|b| b.num_rows()).sum::<usize>();
print_options.print_batches(schema, &results, now, *rows, &Default::default())?;
Ok(())
}