Skip to content

Commit

Permalink
Add support for SQL as input query language
Browse files Browse the repository at this point in the history
The new `\set language sql` toggle switches the CLI into SQL mode.  The
`query` subcommand also gained the `--input-language` argument here,
though it only works with `--tab-separated` output mode.
  • Loading branch information
elprans committed Nov 14, 2024
1 parent dbc9333 commit 3743216
Show file tree
Hide file tree
Showing 13 changed files with 167 additions and 55 deletions.
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ members = [
[package]
name = "edgedb-cli"
license = "MIT/Apache-2.0"
version = "5.5.0-dev"
version = "5.6.0-dev"
authors = ["EdgeDB Inc. <[email protected]>"]
edition = "2018"

Expand Down
6 changes: 5 additions & 1 deletion src/analyze/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ pub async fn interactive(prompt: &mut repl::State, query: &str) -> anyhow::Resul
};
let indesc = data_description.input()?;
let input = match cli
.ping_while(input_variables(&indesc, &mut prompt.prompt))
.ping_while(input_variables(
&indesc,
&mut prompt.prompt,
prompt.input_language,
))
.await
{
Ok(input) => input,
Expand Down
4 changes: 4 additions & 0 deletions src/commands/backslash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,7 @@ pub fn get_setting(s: &Setting, prompt: &repl::State) -> Cow<'static, str> {
"0 # no timeout".into()
}
}
Language(_) => prompt.input_language.as_str().into(),
HistorySize(_) => prompt.history_limit.to_string().into(),
OutputFormat(_) => prompt.output_format.as_str().into(),
DisplayTypenames(_) => bool_str(prompt.display_typenames).into(),
Expand Down Expand Up @@ -645,6 +646,9 @@ pub async fn execute(
let limit = c.value.expect("only set here");
prompt.set_history_limit(limit).await?;
}
Language(l) => {
prompt.input_language = l.value.expect("only writes here");
}
OutputFormat(c) => {
prompt.output_format = c.value.expect("only writes here");
}
Expand Down
8 changes: 8 additions & 0 deletions src/commands/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ pub struct SetCommand {

#[derive(clap::Subcommand, Clone, Debug, EdbSettings)]
pub enum Setting {
/// Query language. One of: edgeql, sql.
Language(Language),
/// Set input mode. One of: vi, emacs
InputMode(InputMode),
/// Print implicit properties of objects: id, type id
Expand Down Expand Up @@ -244,6 +246,12 @@ pub enum Setting {
IdleTransactionTimeout(IdleTransactionTimeout),
}

#[derive(clap::Args, Clone, Debug, Default)]
pub struct Language {
#[arg(value_name = "lang")]
pub value: Option<repl::InputLanguage>,
}

#[derive(clap::Args, Clone, Debug, Default)]
pub struct InputMode {
#[arg(value_name = "mode")]
Expand Down
2 changes: 2 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ pub struct ShellConfig {
#[serde(default, deserialize_with = "parse_idle_tx_timeout")]
pub idle_transaction_timeout: Option<Duration>,
#[serde(with = "serde_str::opt", default)]
pub input_language: Option<repl::InputLanguage>,
#[serde(with = "serde_str::opt", default)]
pub output_format: Option<repl::OutputFormat>,
#[serde(default)]
pub display_typenames: Option<bool>,
Expand Down
19 changes: 12 additions & 7 deletions src/interactive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use edgedb_errors::{ParameterTypeMismatchError, StateMismatchError};
use edgedb_protocol::client_message::Cardinality;
use edgedb_protocol::client_message::CompilationOptions;
use edgedb_protocol::common::RawTypedesc;
use edgedb_protocol::common::{Capabilities, IoFormat, State};
use edgedb_protocol::common::{Capabilities, State};
use edgedb_protocol::descriptors::Typedesc;
use edgedb_protocol::model::Duration;
use edgedb_protocol::value::Value;
Expand Down Expand Up @@ -122,6 +122,10 @@ pub fn main(options: Options, cfg: Config) -> Result<(), anyhow::Error> {
last_analyze: None,
implicit_limit,
idle_transaction_timeout: idle_tx_timeout,
input_language: options
.input_language
.or(cfg.shell.input_language)
.unwrap_or(repl::InputLanguage::EdgeQL),
output_format: options
.output_format
.or(cfg.shell.output_format)
Expand Down Expand Up @@ -276,11 +280,8 @@ async fn execute_query(
implicit_typeids: false,
explicit_objectids: true,
allow_capabilities: Capabilities::ALL,
io_format: match state.output_format {
Default | TabSeparated => IoFormat::Binary,
JsonLines | JsonPretty => IoFormat::JsonElements,
Json => IoFormat::Json,
},
input_language: state.input_language.into(),
io_format: state.output_format.into(),
expected_cardinality: Cardinality::Many,
};

Expand Down Expand Up @@ -310,7 +311,11 @@ async fn execute_query(

let input_start = Instant::now();
let input = match cli
.ping_while(input_variables(&indesc, &mut state.prompt))
.ping_while(input_variables(
&indesc,
&mut state.prompt,
state.input_language,
))
.await
{
Ok(input) => input,
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ fn _main() -> anyhow::Result<()> {
non_interactive::interpret_stdin(
&opt,
opt.output_format.unwrap_or(repl::OutputFormat::JsonPretty),
opt.input_language.unwrap_or(repl::InputLanguage::EdgeQL),
)
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/migrations/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use std::path::Path;

use anyhow::Context as _;
use colorful::Colorful;
use edgedb_protocol::common::{Capabilities, Cardinality, CompilationOptions, IoFormat};
use edgedb_protocol::common::{
Capabilities, Cardinality, CompilationOptions, InputLanguage, IoFormat,
};
use indexmap::IndexMap;
use indicatif::ProgressBar;
use tokio::fs;
Expand Down Expand Up @@ -549,6 +551,7 @@ async fn execute_with_parse_callback(
implicit_typeids: false,
explicit_objectids: true,
allow_capabilities: Capabilities::ALL,
input_language: InputLanguage::EdgeQL,
io_format: IoFormat::Binary,
expected_cardinality: Cardinality::Many,
};
Expand Down
74 changes: 43 additions & 31 deletions src/non_interactive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use terminal_size::{terminal_size, Width};
use tokio::fs::File as AsyncFile;
use tokio::io::{stdin, AsyncRead};

use edgedb_protocol::client_message::Cardinality;
use edgedb_protocol::client_message::CompilationOptions;
use edgedb_protocol::client_message::{Cardinality, IoFormat};
use edgedb_protocol::common::Capabilities;
use edgedb_protocol::value::Value;
use edgeql_parser::preparser;
Expand All @@ -23,7 +23,7 @@ use crate::options::Options;
use crate::options::Query;
use crate::outputs::tab_separated;
use crate::print::{self, PrintError};
use crate::repl::OutputFormat;
use crate::repl;
use crate::statement::{read_statement, EndOfFile};

#[tokio::main(flavor = "current_thread")]
Expand All @@ -42,16 +42,22 @@ pub async fn noninteractive_main(q: &Query, options: &Options) -> Result<(), any
} else {
// Means "native" serialization; for `edgedb query`
// the default is `json-pretty`.
OutputFormat::JsonPretty
repl::OutputFormat::JsonPretty
}
};

let lang = if let Some(l) = q.input_language {
l
} else {
repl::InputLanguage::EdgeQL
};

if let Some(filename) = &q.file {
if filename == "-" {
interpret_file(&mut stdin(), options, fmt).await?;
interpret_file(&mut stdin(), options, fmt, lang).await?;
} else {
let mut file = AsyncFile::open(filename).await?;
interpret_file(&mut file, options, fmt).await?;
interpret_file(&mut file, options, fmt, lang).await?;
}
} else if let Some(queries) = &q.queries {
let mut conn = options.create_connector().await?.connect().await?;
Expand All @@ -62,7 +68,7 @@ pub async fn noninteractive_main(q: &Query, options: &Options) -> Result<(), any
Use the dedicated `edgedb analyze` command."
);
}
run_query(&mut conn, query, options, fmt).await?;
run_query(&mut conn, query, options, fmt, lang).await?;
}
} else {
print::error!(
Expand All @@ -75,14 +81,19 @@ pub async fn noninteractive_main(q: &Query, options: &Options) -> Result<(), any
}

#[tokio::main(flavor = "current_thread")]
pub async fn interpret_stdin(options: &Options, fmt: OutputFormat) -> Result<(), anyhow::Error> {
return interpret_file(&mut stdin(), options, fmt).await;
pub async fn interpret_stdin(
options: &Options,
fmt: repl::OutputFormat,
lang: repl::InputLanguage,
) -> Result<(), anyhow::Error> {
return interpret_file(&mut stdin(), options, fmt, lang).await;
}

async fn interpret_file<T>(
file: &mut T,
options: &Options,
fmt: OutputFormat,
fmt: repl::OutputFormat,
lang: repl::InputLanguage,
) -> Result<(), anyhow::Error>
where
T: AsyncRead + Unpin,
Expand All @@ -105,7 +116,7 @@ where
Use the dedicated `edgedb analyze` command."
);
}
run_query(&mut conn, stmt, options, fmt).await?;
run_query(&mut conn, stmt, options, fmt, lang).await?;
}
Ok(())
}
Expand All @@ -114,25 +125,29 @@ async fn run_query(
conn: &mut Connection,
stmt: &str,
options: &Options,
fmt: OutputFormat,
fmt: repl::OutputFormat,
lang: repl::InputLanguage,
) -> Result<(), anyhow::Error> {
_run_query(conn, stmt, options, fmt).await.map_err(|err| {
if let Some(err) = err.downcast_ref::<edgedb_errors::Error>() {
match print_query_error(err, stmt, false, "<query>") {
Ok(()) => ExitCode::new(1).into(),
Err(e) => e,
_run_query(conn, stmt, options, fmt, lang)
.await
.map_err(|err| {
if let Some(err) = err.downcast_ref::<edgedb_errors::Error>() {
match print_query_error(err, stmt, false, "<query>") {
Ok(()) => ExitCode::new(1).into(),
Err(e) => e,
}
} else {
err
}
} else {
err
}
})
})
}

async fn _run_query(
conn: &mut Connection,
stmt: &str,
_options: &Options,
fmt: OutputFormat,
fmt: repl::OutputFormat,
lang: repl::InputLanguage,
) -> Result<(), anyhow::Error> {
use crate::repl::OutputFormat::*;

Expand All @@ -142,11 +157,8 @@ async fn _run_query(
implicit_typeids: false,
explicit_objectids: true,
allow_capabilities: Capabilities::ALL,
io_format: match fmt {
Default | TabSeparated => IoFormat::Binary,
JsonLines | JsonPretty => IoFormat::JsonElements,
Json => IoFormat::Json,
},
input_language: lang.into(),
io_format: fmt.into(),
expected_cardinality: Cardinality::Many,
};
let data_description = conn.parse(&flags, stmt).await?;
Expand All @@ -170,15 +182,15 @@ async fn _run_query(
}

match fmt {
OutputFormat::TabSeparated => {
repl::OutputFormat::TabSeparated => {
while let Some(row) = items.next().await.transpose()? {
let mut text = tab_separated::format_row(&row)?;
// trying to make writes atomic if possible
text += "\n";
stdout().lock().write_all(text.as_bytes())?;
}
}
OutputFormat::Default => match print::native_to_stdout(&mut items, &cfg).await {
repl::OutputFormat::Default => match print::native_to_stdout(&mut items, &cfg).await {
Ok(()) => {}
Err(e) => {
match e {
Expand All @@ -194,7 +206,7 @@ async fn _run_query(
return Ok(());
}
},
OutputFormat::JsonPretty => {
repl::OutputFormat::JsonPretty => {
while let Some(row) = items.next().await.transpose()? {
let text = match row {
Value::Str(s) => s,
Expand All @@ -213,7 +225,7 @@ async fn _run_query(
stdout().lock().write_all(data.as_bytes())?;
}
}
OutputFormat::JsonLines => {
repl::OutputFormat::JsonLines => {
while let Some(row) = items.next().await.transpose()? {
let mut text = match row {
Value::Str(s) => s,
Expand All @@ -229,7 +241,7 @@ async fn _run_query(
stdout().lock().write_all(text.as_bytes())?;
}
}
OutputFormat::Json => {
repl::OutputFormat::Json => {
while let Some(row) = items.next().await.transpose()? {
let text = match row {
Value::Str(s) => s,
Expand Down
Loading

0 comments on commit 3743216

Please sign in to comment.