mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-28 13:34:24 +01:00
Merge 'Run all statements from sql argument in cli' from Vrishabh
When we had multiple sql statements in cli/interactive shell, we were only running one from them as shown below. This PR fixes them. One added benefit of this is we can add complex sql in the tcl test files like the changes in insert.test . sqlite3 output ``` ❯ sqlite3 :memory: "select 1;select 2" 1 2 ``` Limbo main branch output ``` ❯ ./target/debug/limbo.exe :memory: "select 1;select 2;" 1 ``` Limbos output with this PR ``` ❯ ./target/debug/limbo.exe :memory: "select 1;select 2;" 1 2 ``` Reviewed-by: Preston Thorpe <cory.pride83@gmail.com> Closes #673
This commit is contained in:
41
cli/app.rs
41
cli/app.rs
@@ -3,7 +3,7 @@ use crate::{
|
||||
opcodes_dictionary::OPCODE_DESCRIPTIONS,
|
||||
};
|
||||
use cli_table::{Cell, Table};
|
||||
use limbo_core::{Database, LimboError, StepResult, Value};
|
||||
use limbo_core::{Database, LimboError, Rows, StepResult, Value};
|
||||
|
||||
use clap::{Parser, ValueEnum};
|
||||
use std::{
|
||||
@@ -304,8 +304,14 @@ impl Limbo {
|
||||
fn handle_first_input(&mut self, cmd: &str) {
|
||||
if cmd.trim().starts_with('.') {
|
||||
self.handle_dot_command(cmd);
|
||||
} else if let Err(e) = self.query(cmd) {
|
||||
eprintln!("{}", e);
|
||||
} else {
|
||||
let conn = self.conn.clone();
|
||||
let runner = conn.query_runner(cmd.as_bytes());
|
||||
for output in runner {
|
||||
if let Err(e) = self.print_query_result(cmd, output) {
|
||||
let _ = self.writeln(e.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
std::process::exit(0);
|
||||
}
|
||||
@@ -443,17 +449,16 @@ impl Limbo {
|
||||
self.buffer_input(line);
|
||||
let buff = self.input_buff.clone();
|
||||
let echo = self.opts.echo;
|
||||
buff.split(';')
|
||||
.map(str::trim)
|
||||
.filter(|s| !s.is_empty())
|
||||
.for_each(|stmt| {
|
||||
if echo {
|
||||
let _ = self.writeln(stmt);
|
||||
}
|
||||
if let Err(e) = self.query(stmt) {
|
||||
let _ = self.writeln(e.to_string());
|
||||
}
|
||||
});
|
||||
if echo {
|
||||
let _ = self.writeln(&buff);
|
||||
}
|
||||
let conn = self.conn.clone();
|
||||
let runner = conn.query_runner(buff.as_bytes());
|
||||
for output in runner {
|
||||
if let Err(e) = self.print_query_result(&buff, output) {
|
||||
let _ = self.writeln(e.to_string());
|
||||
}
|
||||
}
|
||||
self.reset_input();
|
||||
} else {
|
||||
self.buffer_input(line);
|
||||
@@ -570,8 +575,12 @@ impl Limbo {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query(&mut self, sql: &str) -> anyhow::Result<()> {
|
||||
match self.conn.query(sql) {
|
||||
fn print_query_result(
|
||||
&mut self,
|
||||
sql: &str,
|
||||
mut output: Result<Option<Rows>, LimboError>,
|
||||
) -> anyhow::Result<()> {
|
||||
match output {
|
||||
Ok(Some(ref mut rows)) => match self.opts.output_mode {
|
||||
OutputMode::Raw => loop {
|
||||
if self.interrupt_count.load(Ordering::SeqCst) > 0 {
|
||||
|
||||
126
core/lib.rs
126
core/lib.rs
@@ -298,57 +298,65 @@ impl Connection {
|
||||
pub fn query(self: &Rc<Connection>, sql: impl Into<String>) -> Result<Option<Rows>> {
|
||||
let sql = sql.into();
|
||||
trace!("Querying: {}", sql);
|
||||
let db = self.db.clone();
|
||||
let syms: &SymbolTable = &db.syms.borrow();
|
||||
let mut parser = Parser::new(sql.as_bytes());
|
||||
let cmd = parser.next()?;
|
||||
if let Some(cmd) = cmd {
|
||||
match cmd {
|
||||
Cmd::Stmt(stmt) => {
|
||||
let program = Rc::new(translate::translate(
|
||||
&self.schema.borrow(),
|
||||
stmt,
|
||||
self.header.clone(),
|
||||
self.pager.clone(),
|
||||
Rc::downgrade(self),
|
||||
syms,
|
||||
)?);
|
||||
let stmt = Statement::new(program, self.pager.clone());
|
||||
Ok(Some(Rows { stmt }))
|
||||
}
|
||||
Cmd::Explain(stmt) => {
|
||||
let program = translate::translate(
|
||||
&self.schema.borrow(),
|
||||
stmt,
|
||||
self.header.clone(),
|
||||
self.pager.clone(),
|
||||
Rc::downgrade(self),
|
||||
syms,
|
||||
)?;
|
||||
program.explain();
|
||||
Ok(None)
|
||||
}
|
||||
Cmd::ExplainQueryPlan(stmt) => {
|
||||
match stmt {
|
||||
ast::Stmt::Select(select) => {
|
||||
let mut plan = prepare_select_plan(
|
||||
&self.schema.borrow(),
|
||||
*select,
|
||||
&self.db.syms.borrow(),
|
||||
)?;
|
||||
optimize_plan(&mut plan)?;
|
||||
println!("{}", plan);
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
match cmd {
|
||||
Some(cmd) => self.run_cmd(cmd),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn run_cmd(self: &Rc<Connection>, cmd: Cmd) -> Result<Option<Rows>> {
|
||||
let db = self.db.clone();
|
||||
let syms: &SymbolTable = &db.syms.borrow();
|
||||
|
||||
match cmd {
|
||||
Cmd::Stmt(stmt) => {
|
||||
let program = Rc::new(translate::translate(
|
||||
&self.schema.borrow(),
|
||||
stmt,
|
||||
self.header.clone(),
|
||||
self.pager.clone(),
|
||||
Rc::downgrade(self),
|
||||
syms,
|
||||
)?);
|
||||
let stmt = Statement::new(program, self.pager.clone());
|
||||
Ok(Some(Rows { stmt }))
|
||||
}
|
||||
Cmd::Explain(stmt) => {
|
||||
let program = translate::translate(
|
||||
&self.schema.borrow(),
|
||||
stmt,
|
||||
self.header.clone(),
|
||||
self.pager.clone(),
|
||||
Rc::downgrade(self),
|
||||
syms,
|
||||
)?;
|
||||
program.explain();
|
||||
Ok(None)
|
||||
}
|
||||
Cmd::ExplainQueryPlan(stmt) => {
|
||||
match stmt {
|
||||
ast::Stmt::Select(select) => {
|
||||
let mut plan = prepare_select_plan(
|
||||
&self.schema.borrow(),
|
||||
*select,
|
||||
&self.db.syms.borrow(),
|
||||
)?;
|
||||
optimize_plan(&mut plan)?;
|
||||
println!("{}", plan);
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query_runner<'a>(self: &'a Rc<Connection>, sql: &'a [u8]) -> QueryRunner<'a> {
|
||||
QueryRunner::new(self, sql)
|
||||
}
|
||||
|
||||
pub fn execute(self: &Rc<Connection>, sql: impl Into<String>) -> Result<()> {
|
||||
let sql = sql.into();
|
||||
let db = self.db.clone();
|
||||
@@ -560,3 +568,29 @@ impl SymbolTable {
|
||||
self.functions.get(name).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QueryRunner<'a> {
|
||||
parser: Parser<'a>,
|
||||
conn: &'a Rc<Connection>,
|
||||
}
|
||||
|
||||
impl<'a> QueryRunner<'a> {
|
||||
pub(crate) fn new(conn: &'a Rc<Connection>, statements: &'a [u8]) -> Self {
|
||||
Self {
|
||||
parser: Parser::new(statements),
|
||||
conn,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for QueryRunner<'_> {
|
||||
type Item = Result<Option<Rows>>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.parser.next() {
|
||||
Ok(Some(cmd)) => Some(self.conn.run_cmd(cmd)),
|
||||
Ok(None) => None,
|
||||
Err(err) => Some(Result::Err(LimboError::from(err))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
#!/usr/bin/env tclsh
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_execsql_test_on_specific_db {:memory:} basic-insert {
|
||||
create table temp (t1 integer, primary key (t1));
|
||||
insert into temp values (1);
|
||||
select * from temp;
|
||||
} {1}
|
||||
Reference in New Issue
Block a user