diff --git a/prqlc/prqlc-parser/src/parser/pr/expr.rs b/prqlc/prqlc-parser/src/parser/pr/expr.rs index 5dfabb3a28f1..a737372198bf 100644 --- a/prqlc/prqlc-parser/src/parser/pr/expr.rs +++ b/prqlc/prqlc-parser/src/parser/pr/expr.rs @@ -97,6 +97,18 @@ impl ExprKind { doc_comment: None, } } + + /// Whether it contains spaces between top-level items. + /// So, for example `sum foo` would be true, but `[foo, bar]` would be + /// false, since the array is self-contained. + pub fn is_multiple_items(&self) -> bool { + match self { + ExprKind::Binary(_) => true, + ExprKind::Func(_) => true, + ExprKind::FuncCall(func_call) if !func_call.args.is_empty() => true, + _ => false, + } + } } #[derive(Debug, EnumAsInner, PartialEq, Clone, Serialize, Deserialize, JsonSchema)] diff --git a/prqlc/prqlc-parser/src/snapshots/prqlc_parser__test__pipeline_parse_tree.snap b/prqlc/prqlc-parser/src/snapshots/prqlc_parser__test__pipeline_parse_tree.snap index 38604e50876c..493b2790fe20 100644 --- a/prqlc/prqlc-parser/src/snapshots/prqlc_parser__test__pipeline_parse_tree.snap +++ b/prqlc/prqlc-parser/src/snapshots/prqlc_parser__test__pipeline_parse_tree.snap @@ -1,6 +1,6 @@ --- source: prqlc/prqlc-parser/src/test.rs -expression: "parse_single(r#\"\nfrom employees\nfilter country == \"USA\" # Each line transforms the previous result.\nderive { # This adds columns / variables.\n gross_salary = salary + payroll_tax,\n gross_cost = gross_salary + benefits_cost # Variables can use other variables.\n}\nfilter gross_cost > 0\ngroup {title, country} ( # For each group use a nested pipeline\n aggregate { # Aggregate each group to a single row\n average salary,\n average gross_salary,\n sum salary,\n sum gross_salary,\n average gross_cost,\n sum_gross_cost = sum gross_cost,\n ct = count salary,\n }\n)\nsort sum_gross_cost\nfilter ct > 200\ntake 20\n \"#).unwrap()" +expression: "parse_source(r#\"\nfrom employees\nfilter country == \"USA\" # Each line transforms the previous result.\nderive { # This adds columns / variables.\n gross_salary = salary + payroll_tax,\n gross_cost = gross_salary + benefits_cost # Variables can use other variables.\n}\nfilter gross_cost > 0\ngroup {title, country} ( # For each group use a nested pipeline\n aggregate { # Aggregate each group to a single row\n average salary,\n average gross_salary,\n sum salary,\n sum gross_salary,\n average gross_cost,\n sum_gross_cost = sum gross_cost,\n ct = count salary,\n }\n)\nsort sum_gross_cost\nfilter ct > 200\ntake 20\n \"#).unwrap()" --- - VarDef: kind: Main diff --git a/prqlc/prqlc/src/codegen/ast.rs b/prqlc/prqlc/src/codegen/ast.rs index 8cbdfcdcf0ba..e0e0e516cb2d 100644 --- a/prqlc/prqlc/src/codegen/ast.rs +++ b/prqlc/prqlc/src/codegen/ast.rs @@ -1,6 +1,7 @@ use std::collections::HashSet; use std::sync::OnceLock; +use prqlc_parser::parser::pr::FuncCall; use regex::Regex; use super::{WriteOpt, WriteSource}; @@ -26,28 +27,64 @@ impl WriteSource for pr::Expr { fn write(&self, mut opt: WriteOpt) -> Option { let mut r = String::new(); + // If there's an alias, then the expr can't be unbound on its left (we + // set this before evaluating parentheses) + if self.alias.is_some() { + opt.unbound_expr = false; + } + + // We need to know before we print the alias whether our `.kind` needs + // parentheses, because a parethenized value is a single_expr and + // doesn't need a space around the alias' `=`. + let kind_needs_parens = needs_parenthesis(self, &opt.clone()); + let single_expr = kind_needs_parens || !self.kind.is_multiple_items(); + if let Some(alias) = &self.alias { r += opt.consume(alias)?; - r += opt.consume(" = ")?; - opt.unbound_expr = false; + r += opt.consume(if single_expr { "=" } else { " = " })?; } - if !needs_parenthesis(self, &opt) { - r += &self.kind.write(opt.clone())?; + if !kind_needs_parens { + r += &self.kind.write(opt.clone())? } else { let value = self.kind.write_between("(", ")", opt.clone()); if let Some(value) = value { - r += &value; + r += &value } else { - r += &break_line_within_parenthesis(&self.kind, opt)?; + r += &break_line_within_parenthesis(&self.kind, opt.clone())? } }; + + opt.single_term = match &self.kind { + // If this item is within an array / tuple, then the nested item is a + // single term, for example `derive {x = sum listens}`. + pr::ExprKind::Array(_) | pr::ExprKind::Tuple(_) => true, + // If it's a single arg in a function call, that's a single term, + // like `derive x = foo` (but not like `derive x=(sum foo)`) + pr::ExprKind::FuncCall(FuncCall { + args, named_args, .. + }) => dbg!(args.len() + named_args.len() == 1), + _ => false, + }; + + dbg!((&r, &self, opt)); + Some(r) } } fn needs_parenthesis(this: &pr::Expr, opt: &WriteOpt) -> bool { + // If we have an alias, we use parentheses if we contain multiple items, so + // we get `a=(b + c)` instead of `a=b + c`. + // + // The exception if we're within a single term, then this doesn't apply (we + // don't return true here) — so then we get `derive {a = b + c}` rather than + // `derive {a=(b + c)}`. + if this.alias.is_some() && this.kind.is_multiple_items() && !opt.single_term { + return true; + } + if opt.unbound_expr && can_bind_left(&this.kind) { return true; } @@ -68,12 +105,12 @@ fn needs_parenthesis(this: &pr::Expr, opt: &WriteOpt) -> bool { // parent has equal binding strength, which means that now associativity of this expr counts // for example: // this=(a + b), parent=(a + b) + c - // asoc of + is left + // assoc of + is left // this is the left operand of parent // => assoc_matches=true => we don't need parenthesis // this=(a + b), parent=c + (a + b) - // asoc of + is left + // assoc of + is left // this is the right operand of parent // => assoc_matches=false => we need parenthesis let assoc_matches = match opt.binary_position { @@ -474,7 +511,7 @@ impl WriteSource for pr::Stmt { r += opt.consume(&format!("type {}", type_def.name))?; if let Some(ty) = &type_def.value { - r += opt.consume(" = ")?; + r += opt.consume("=")?; r += &ty.kind.write(opt)?; } r += "\n"; @@ -493,7 +530,7 @@ impl WriteSource for pr::Stmt { r += "import "; if let Some(alias) = &import_def.alias { r += &write_ident_part(alias); - r += " = "; + r += "="; } r += &import_def.name.write(opt)?; r += "\n"; @@ -614,7 +651,7 @@ mod test { fn test_unary() { assert_is_formatted(r#"sort {-duration}"#); - assert_is_formatted(r#"select a = -b"#); + assert_is_formatted(r#"select a=-b"#); assert_is_formatted(r#"join `project-bar.dataset.table` (==col_bax)"#); } @@ -632,15 +669,20 @@ mod test { #[test] fn test_func() { assert_is_formatted(r#"let a = func x y:false -> x and y"#); + // See notes in [WriteOpt::single_term] + assert_is_formatted(r#"derive rounded=(round 2 gross_cost)"#); + assert_is_formatted(r#"aggregate {sum_gross_cost = sum gross_cost}"#); + // Not sure about these? TODO: decide + // assert_is_formatted(r#"derive foo = 2 + 3"#); + assert_is_formatted(r#"derive foo=(2 + 3)"#); } #[test] fn test_simple() { assert_is_formatted( r#" -aggregate average_country_salary = ( - average salary -)"#, +aggregate average_country_salary=(average salary) +"#, ); } diff --git a/prqlc/prqlc/src/codegen/mod.rs b/prqlc/prqlc/src/codegen/mod.rs index 125fb0b1c1ef..c1dd7af95ed9 100644 --- a/prqlc/prqlc/src/codegen/mod.rs +++ b/prqlc/prqlc/src/codegen/mod.rs @@ -21,6 +21,7 @@ pub trait WriteSource { r += opt.consume(&prefix.to_string())?; opt.context_strength = 0; opt.unbound_expr = false; + opt.single_term = true; let source = self.write(opt.clone())?; r += opt.consume(&source)?; @@ -47,7 +48,7 @@ impl WriteSource for &T { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct WriteOpt { /// String to emit as one indentation level pub tab: &'static str, @@ -74,11 +75,23 @@ pub struct WriteOpt { /// be mistakenly bound into a binary op by appending an unary op. /// /// For example: - /// `join foo` has an unbound expr, since `join foo ==bar` produced a binary op. + /// `join foo` has an unbound expr, since `join foo ==bar` produced a binary + /// op + /// ...so we need to parenthesize the `==bar`. pub unbound_expr: bool, + + /// True iff the expression is surrounded by spaces and so we prefer to + /// format with parentheses. For example: + /// - the function call in `derive rounded=(round 2 gross_cost)` is + /// formatted without spaces around the `=` because it's a single term. + /// - the function call in `derive foo = 2 + 3` is formatted with spaces + /// around the `=` because it's not a single term + /// - the function call in `aggregate {sum_gross_cost = sum gross_cost}` is + /// formatted with spaces around the `=` because it's not a single term. + pub single_term: bool, } -#[derive(Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Position { Unspecified, Left, @@ -96,6 +109,7 @@ impl Default for WriteOpt { context_strength: 0, binary_position: Position::Unspecified, unbound_expr: false, + single_term: false, } } } diff --git a/prqlc/prqlc/src/codegen/types.rs b/prqlc/prqlc/src/codegen/types.rs index e920130f2342..eae3657804f2 100644 --- a/prqlc/prqlc/src/codegen/types.rs +++ b/prqlc/prqlc/src/codegen/types.rs @@ -101,7 +101,7 @@ impl WriteSource for pr::TyTupleField { if let Some(name) = name { r += name; - r += " = "; + r += "="; } if let Some(expr) = expr { r += &expr.write(opt)?; @@ -121,7 +121,7 @@ impl WriteSource for UnionVariant<'_> { let mut r = String::new(); if let Some(name) = &self.0 { r += name; - r += " = "; + r += "="; } opt.consume_width(r.len() as u16); r += &self.1.write(opt)?; diff --git a/prqlc/prqlc/src/semantic/eval.rs b/prqlc/prqlc/src/semantic/eval.rs index 531140c8f9fb..9376c6df3522 100644 --- a/prqlc/prqlc/src/semantic/eval.rs +++ b/prqlc/prqlc/src/semantic/eval.rs @@ -482,7 +482,7 @@ mod test { assert_snapshot!(eval(r" {{a_a = 4, a_b = false}, b = 2.1 + 3.6, c = [false, true, false]} ").unwrap(), - @"{{a_a = 4, a_b = false}, b = 5.7, c = [false, true, false]}" + @"{{a_a=4, a_b=false}, b=5.7, c=[false, true, false]}" ); } @@ -507,7 +507,7 @@ mod test { std.derive {d = 42} std.filter c ").unwrap(), - @"[{c = true, 7, d = 42}, {c = true, 14, d = 42}]" + @"[{c=true, 7, d=42}, {c=true, 14, d=42}]" ); } @@ -521,7 +521,7 @@ mod test { ] std.window {d = std.sum b} ").unwrap(), - @"[{d = 4}, {d = 9}, {d = 17}]" + @"[{d=4}, {d=9}, {d=17}]" ); } @@ -535,7 +535,7 @@ mod test { ] std.columnar {g = std.lag b} ").unwrap(), - @"[{g = null}, {g = 4}, {g = 5}]" + @"[{g=null}, {g=4}, {g=5}]" ); } } diff --git a/prqlc/prqlc/tests/integration/project/Project.prql b/prqlc/prqlc/tests/integration/project/Project.prql index 32af7f312e0c..73e1730a7bfb 100644 --- a/prqlc/prqlc/tests/integration/project/Project.prql +++ b/prqlc/prqlc/tests/integration/project/Project.prql @@ -1,6 +1,6 @@ let favorite_artists = [ - {artist_id = 120, last_listen = @2023-05-18}, - {artist_id = 7, last_listen = @2023-05-16}, + {artist_id=120, last_listen=@2023-05-18}, + {artist_id=7, last_listen=@2023-05-16}, ] favorite_artists diff --git a/prqlc/prqlc/tests/integration/resolving.rs b/prqlc/prqlc/tests/integration/resolving.rs index 170b613a96a7..ab3a727a8db6 100644 --- a/prqlc/prqlc/tests/integration/resolving.rs +++ b/prqlc/prqlc/tests/integration/resolving.rs @@ -31,7 +31,7 @@ fn resolve_basic_01() { from x select {a, b} "#).unwrap(), @r###" - let main <[{a = ?, b = ?}]> = `(Select ...)` + let main <[{a=?, b=?}]> = `(Select ...)` "###) } @@ -53,7 +53,7 @@ fn resolve_types_01() { assert_snapshot!(resolve(r#" type A = int || int "#).unwrap(), @r###" - type A = int + type A=int "###) } @@ -62,7 +62,7 @@ fn resolve_types_02() { assert_snapshot!(resolve(r#" type A = int || {} "#).unwrap(), @r###" - type A = int || {} + type A=int || {} "###) } @@ -71,7 +71,7 @@ fn resolve_types_03() { assert_snapshot!(resolve(r#" type A = {a = int, bool} || {b = text, float} "#).unwrap(), @r###" - type A = {a = int, bool, b = text, float} + type A={a=int, bool, b=text, float} "###) } @@ -87,9 +87,9 @@ fn resolve_types_04() { "#, ) .unwrap(), @r###" - type Status = ( - Unpaid = float || - {reason = text, cancelled_at = timestamp} || + type Status=( + Unpaid=float || + {reason=text, cancelled_at=timestamp} || ) "###); } @@ -103,7 +103,7 @@ fn resolve_types_05() { "#, ) .unwrap(), @r###" - type A = null + type A=null "###); } diff --git a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__aggregation.snap b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__aggregation.snap index d50e2057a843..048431ad40e8 100644 --- a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__aggregation.snap +++ b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__aggregation.snap @@ -5,11 +5,10 @@ input_file: prqlc/prqlc/tests/integration/queries/aggregation.prql --- from tracks filter genre_id == 100 -derive empty_name = name == "" +derive empty_name=(name == "") aggregate { sum track_id, concat_array name, all empty_name, any empty_name, } - diff --git a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__arithmetic.snap b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__arithmetic.snap index 6be21fc08eff..a77b60a18016 100644 --- a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__arithmetic.snap +++ b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__arithmetic.snap @@ -4,33 +4,27 @@ expression: "# mssql:test\nfrom [\n { id = 1, x_int = 13, x_float = 13.0, k input_file: prqlc/prqlc/tests/integration/queries/arithmetic.prql --- from [ + {id=1, x_int=13, x_float=13, k_int=5, k_float=5}, { - id = 1, - x_int = 13, - x_float = 13, - k_int = 5, - k_float = 5, + id=2, + x_int=-13, + x_float=-13, + k_int=5, + k_float=5, }, { - id = 2, - x_int = -13, - x_float = -13, - k_int = 5, - k_float = 5, + id=3, + x_int=13, + x_float=13, + k_int=-5, + k_float=-5, }, { - id = 3, - x_int = 13, - x_float = 13, - k_int = -5, - k_float = -5, - }, - { - id = 4, - x_int = -13, - x_float = -13, - k_int = -5, - k_float = -5, + id=4, + x_int=-13, + x_float=-13, + k_int=-5, + k_float=-5, }, ] select { @@ -53,4 +47,3 @@ select { (q_ff * k_float + r_ff | math.round 0), } sort id - diff --git a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__cast.snap b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__cast.snap index 1f5eed50818b..6acb82527f54 100644 --- a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__cast.snap +++ b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__cast.snap @@ -7,4 +7,3 @@ from tracks sort {-bytes} select {name, bin = (album_id | as REAL) * 99} take 20 - diff --git a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__constants_only.snap b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__constants_only.snap index 79dccd1dc029..d4ac69c8b99e 100644 --- a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__constants_only.snap +++ b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__constants_only.snap @@ -8,5 +8,4 @@ take 10 filter true take 20 filter true -select d = 10 - +select d=10 diff --git a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__date_to_text.snap b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__date_to_text.snap index e558891fddce..d69c7a67392e 100644 --- a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__date_to_text.snap +++ b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__date_to_text.snap @@ -6,17 +6,22 @@ input_file: prqlc/prqlc/tests/integration/queries/date_to_text.prql from invoices take 20 select { - d1 = (invoice_date | date.to_text "%Y/%m/%d"), - d2 = (invoice_date | date.to_text "%F"), - d3 = (invoice_date | date.to_text "%D"), - d4 = (invoice_date | date.to_text "%H:%M:%S.%f"), - d5 = (invoice_date | date.to_text "%r"), - d6 = (invoice_date | date.to_text "%A %B %-d %Y"), - d7 = (invoice_date | date.to_text "%a, %-d %b %Y at %I:%M:%S %p"), - d8 = (invoice_date | date.to_text "%+"), - d9 = (invoice_date | date.to_text "%-d/%-m/%y"), - d10 = (invoice_date | date.to_text "%-Hh %Mmin"), - d11 = (invoice_date | date.to_text ""%M'%S"""), - d12 = (invoice_date | date.to_text "100%% in %d days"), + d1=(invoice_date | date.to_text "%Y/%m/%d"), + d2=(invoice_date | date.to_text "%F"), + d3=(invoice_date | date.to_text "%D"), + d4=(invoice_date | date.to_text "%H:%M:%S.%f"), + d5=(invoice_date | date.to_text "%r"), + d6=(invoice_date | date.to_text "%A %B %-d %Y"), + d7=( + invoice_date + date.to_text "%a, %-d %b %Y at %I:%M:%S %p" + ), + d8=(invoice_date | date.to_text "%+"), + d9=(invoice_date | date.to_text "%-d/%-m/%y"), + d10=(invoice_date | date.to_text "%-Hh %Mmin"), + d11=(invoice_date | date.to_text ""%M'%S"""), + d12=( + invoice_date + date.to_text "100%% in %d days" + ), } - diff --git a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__genre_counts.snap b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__genre_counts.snap index fb73ace8a03a..0dff340b3032 100644 --- a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__genre_counts.snap +++ b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__genre_counts.snap @@ -10,5 +10,4 @@ let genre_count = ( from genre_count filter a > 0 -select a = -a - +select a=-a diff --git a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__group_all.snap b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__group_all.snap index 933d8efda9a2..706c63dd7606 100644 --- a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__group_all.snap +++ b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__group_all.snap @@ -3,12 +3,11 @@ source: prqlc/prqlc/tests/integration/queries.rs expression: "# mssql:test\nfrom a=albums\ntake 10\njoin tracks (==album_id)\ngroup {a.album_id, a.title} (aggregate price = (sum tracks.unit_price | math.round 2))\nsort album_id\n" input_file: prqlc/prqlc/tests/integration/queries/group_all.prql --- -from a = albums +from a=albums take 10 join tracks (==album_id) -group {a.album_id, a.title} (aggregate price = ( +group {a.album_id, a.title} (aggregate price=( sum tracks.unit_price math.round 2 )) sort album_id - diff --git a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__group_sort.snap b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__group_sort.snap index f1013da6e4a5..a550bb6e3e55 100644 --- a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__group_sort.snap +++ b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__group_sort.snap @@ -4,9 +4,8 @@ expression: "# mssql:test\nfrom tracks\nderive d = album_id + 1\ngroup d (\n input_file: prqlc/prqlc/tests/integration/queries/group_sort.prql --- from tracks -derive d = album_id + 1 -group d (aggregate {n1 = (track_id | sum)}) +derive d=(album_id + 1) +group d (aggregate {n1=(track_id | sum)}) sort d take 10 -select {d1 = d, n1} - +select {d1=d, n1} diff --git a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__invoice_totals.snap b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__invoice_totals.snap index 7793d0d10f29..1f0b8f6abb4e 100644 --- a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__invoice_totals.snap +++ b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__invoice_totals.snap @@ -3,11 +3,11 @@ source: prqlc/prqlc/tests/integration/queries.rs expression: "# clickhouse:skip (clickhouse doesn't have lag function)\n\n#! Calculate a number of metrics about the sales of tracks in each city.\nfrom i=invoices\njoin ii=invoice_items (==invoice_id)\nderive {\n city = i.billing_city,\n street = i.billing_address,\n}\ngroup {city, street} (\n derive total = ii.unit_price * ii.quantity\n aggregate {\n num_orders = count_distinct i.invoice_id,\n num_tracks = sum ii.quantity,\n total_price = sum total,\n }\n)\ngroup {city} (\n sort street\n window expanding:true (\n derive {running_total_num_tracks = sum num_tracks}\n )\n)\nsort {city, street}\nderive {num_tracks_last_week = lag 7 num_tracks}\nselect {\n city,\n street,\n num_orders,\n num_tracks,\n running_total_num_tracks,\n num_tracks_last_week\n}\ntake 20\n" input_file: prqlc/prqlc/tests/integration/queries/invoice_totals.prql --- -from i = invoices -join ii = invoice_items (==invoice_id) +from i=invoices +join ii=invoice_items (==invoice_id) derive { - city = i.billing_city, - street = i.billing_address, + city=i.billing_city, + street=i.billing_address, } group {city, street} ( derive total = ii.unit_price * ii.quantity @@ -34,4 +34,3 @@ select { num_tracks_last_week, } take 20 - diff --git a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__loop_01.snap b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__loop_01.snap index 8e9776e19d6d..c51d4dad736b 100644 --- a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__loop_01.snap +++ b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__loop_01.snap @@ -3,9 +3,8 @@ source: prqlc/prqlc/tests/integration/queries.rs expression: "# clickhouse:skip (DB::Exception: Syntax error)\n# glaredb:skip (DataFusion does not support recursive CTEs https://github.com/apache/arrow-datafusion/issues/462)\nfrom [{n = 1}]\nselect n = n - 2\nloop (filter n < 4 | select n = n + 1)\nselect n = n * 2\nsort n\n" input_file: prqlc/prqlc/tests/integration/queries/loop_01.prql --- -from [{n = 1}] +from [{n=1}] select n = n - 2 loop (filter n < 4 | select n = n + 1) select n = n * 2 sort n - diff --git a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__math_module.snap b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__math_module.snap index 96b8baeacacb..8dff17a31fd6 100644 --- a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__math_module.snap +++ b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__math_module.snap @@ -6,47 +6,43 @@ input_file: prqlc/prqlc/tests/integration/queries/math_module.prql from invoices take 5 select { - total_original = (total | math.round 2), - total_x = ( + total_original=(total | math.round 2), + total_x=( math.pi - total math.round 2 math.abs ), total_floor = math.floor total, total_ceil = math.ceil total, - total_log10 = (math.log10 total | math.round 3), - total_log2 = (math.log 2 total | math.round 3), - total_sqrt = (math.sqrt total | math.round 3), - total_ln = ( + total_log10=(math.log10 total | math.round 3), + total_log2=(math.log 2 total | math.round 3), + total_sqrt=(math.sqrt total | math.round 3), + total_ln=( math.ln total math.exp math.round 2 ), - total_cos = ( + total_cos=( math.cos total math.acos math.round 2 ), - total_sin = ( + total_sin=( math.sin total math.asin math.round 2 ), - total_tan = ( + total_tan=( math.tan total math.atan math.round 2 ), - total_deg = ( + total_deg=( total math.degrees math.radians math.round 2 ), - total_square = ( - total - math.pow 2 - math.round 2 - ), - total_square_op = (total ** 2 | math.round 2), + total_square=(total | math.pow 2 | math.round 2), + total_square_op=(total ** 2 | math.round 2), } diff --git a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__set_ops_remove.snap b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__set_ops_remove.snap index 47efa07907e9..f3e3eeeb9078 100644 --- a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__set_ops_remove.snap +++ b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__set_ops_remove.snap @@ -4,7 +4,7 @@ expression: "# mssql:test\nlet distinct = rel -> (from t = _param.rel | group {t input_file: prqlc/prqlc/tests/integration/queries/set_ops_remove.prql --- let distinct = func rel -> ( - from t = _param.rel + from t=_param.rel group {t.*} (take 1) ) @@ -12,4 +12,3 @@ from_text format:json '{ "columns": ["a"], "data": [[1], [2], [2], [3]] }' distinct remove (from_text format:json '{ "columns": ["a"], "data": [[1], [2]] }') sort a - diff --git a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__sort.snap b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__sort.snap index 8bb61f7b6f0b..9fdd8603d69d 100644 --- a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__sort.snap +++ b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__sort.snap @@ -3,9 +3,8 @@ source: prqlc/prqlc/tests/integration/queries.rs expression: "# mssql:test\nfrom e=employees\nfilter first_name != \"Mitchell\"\nsort {first_name, last_name}\n\n# joining may use HashMerge, which can undo ORDER BY\njoin manager=employees side:left (e.reports_to == manager.employee_id)\n\nselect {e.first_name, e.last_name, manager.first_name}\n" input_file: prqlc/prqlc/tests/integration/queries/sort.prql --- -from e = employees +from e=employees filter first_name != "Mitchell" sort {first_name, last_name} -join side:left manager = employees e.reports_to == manager.employee_id +join side:left manager=employees e.reports_to == manager.employee_id select {e.first_name, e.last_name, manager.first_name} - diff --git a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__switch.snap b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__switch.snap index 83daf4d25cc8..12a24290a5a5 100644 --- a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__switch.snap +++ b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__switch.snap @@ -5,10 +5,9 @@ input_file: prqlc/prqlc/tests/integration/queries/switch.prql --- from tracks sort milliseconds -select display = case [ +select display=case [ composer != null => composer, genre_id < 17 => "no composer", true => f"unknown composer", ] take 10 - diff --git a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__text_module.snap b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__text_module.snap index 9aed24f481bc..a44c0ed4fb31 100644 --- a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__text_module.snap +++ b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__text_module.snap @@ -6,15 +6,15 @@ input_file: prqlc/prqlc/tests/integration/queries/text_module.prql from albums select { title, - title_and_spaces = f" {title} ", - low = (title | text.lower), - up = (title | text.upper), - ltrimmed = (title | text.ltrim), - rtrimmed = (title | text.rtrim), - trimmed = (title | text.trim), - len = (title | text.length), - subs = (title | text.extract 2 5), - replace = (title | text.replace "al" "PIKA"), + title_and_spaces=f" {title} ", + low=(title | text.lower), + up=(title | text.upper), + ltrimmed=(title | text.ltrim), + rtrimmed=(title | text.rtrim), + trimmed=(title | text.trim), + len=(title | text.length), + subs=(title | text.extract 2 5), + replace=(title | text.replace "al" "PIKA"), } sort {title} filter (title | text.starts_with "Black") || ( diff --git a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__window.snap b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__window.snap index b062e5c5bbda..6b6f6d799841 100644 --- a/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__window.snap +++ b/prqlc/prqlc/tests/integration/snapshots/integration__queries__fmt__window.snap @@ -1,6 +1,6 @@ --- source: prqlc/prqlc/tests/integration/queries.rs -expression: "# mssql:skip Conversion(\"cannot interpret I64(Some(1)) as an i32 value\")', connection.rs:200:34\n# duckdb:skip problems with DISTINCT ON (duckdb internal error: [with INPUT_TYPE = int; RESULT_TYPE = unsigned char]: Assertion `min_val <= input' failed.)\n# clickhouse:skip problems with DISTINCT ON\n# postgres:skip problems with DISTINCT ON\nfrom tracks\ngroup genre_id (\n sort milliseconds\n derive {\n num = row_number this,\n total = count this,\n last_val = last track_id,\n }\n take 10\n)\nsort {genre_id, milliseconds}\nselect {track_id, genre_id, num, total, last_val}\nfilter genre_id >= 22\n" +expression: "# mssql:skip Conversion(\"cannot interpret I64(Some(1)) as an i32 value\")', connection.rs:200:34\n# duckdb:skip problems with DISTINCT ON (duckdb internal error: [with INPUT_TYPE = int; RESULT_TYPE = unsigned char]: Assertion `min_val <= input' failed.)\n# clickhouse:skip problems with DISTINCT ON\n# postgres:skip problems with DISTINCT ON\n# glaredb:skip — TODO: started raising an error on 2024-05-20, from https://github.com/PRQL/prql/actions/runs/9154902656/job/25198160283:\n # ERROR: This feature is not implemented: Unsupported ast node in sqltorel:\n # Substring { expr: Identifier(Ident { value: \"title\", quote_style: None }),\n # substring_from: Some(Value(Number(\"2\", false))), substring_for:\n # Some(Value(Number(\"5\", false))), special: true }\nfrom tracks\ngroup genre_id (\n sort milliseconds\n derive {\n num = row_number this,\n total = count this,\n last_val = last track_id,\n }\n take 10\n)\nsort {genre_id, milliseconds}\nselect {track_id, genre_id, num, total, last_val}\nfilter genre_id >= 22\n" input_file: prqlc/prqlc/tests/integration/queries/window.prql --- from tracks @@ -16,4 +16,3 @@ group genre_id ( sort {genre_id, milliseconds} select {track_id, genre_id, num, total, last_val} filter genre_id >= 22 - diff --git a/prqlc/prqlc/tests/integration/sql.rs b/prqlc/prqlc/tests/integration/sql.rs index 8ce061430fa5..37e7fe6e5a11 100644 --- a/prqlc/prqlc/tests/integration/sql.rs +++ b/prqlc/prqlc/tests/integration/sql.rs @@ -2836,8 +2836,8 @@ aggregate { # `by` are the columns to group by. average gross_salary, sum gross_salary, average gross_cost, - sum_gross_cost = sum gross_cost, - ct = count salary, + sum_gross_cost=(sum gross_cost), + ct=(count salary), } ) sort sum_gross_cost diff --git a/web/book/src/README.md b/web/book/src/README.md index 9cb47d984fb3..9b80b9b38268 100644 --- a/web/book/src/README.md +++ b/web/book/src/README.md @@ -49,7 +49,7 @@ filter gross_cost > 0 group {title, country} ( # `group` runs a pipeline over each group aggregate { # `aggregate` reduces each group to a value average gross_salary, - sum_gross_cost = sum gross_cost, # `=` sets a column name + sum_gross_cost=(sum gross_cost), # `=` sets a column name } ) filter sum_gross_cost > 100_000 # `filter` replaces both of SQL's `WHERE` & `HAVING` diff --git a/web/book/src/reference/stdlib/transforms/aggregate.md b/web/book/src/reference/stdlib/transforms/aggregate.md index 303ed80b186f..61cf70915fa3 100644 --- a/web/book/src/reference/stdlib/transforms/aggregate.md +++ b/web/book/src/reference/stdlib/transforms/aggregate.md @@ -32,7 +32,7 @@ from employees group {title, country} ( aggregate { average salary, - ct = count salary, + ct=(count salary), } ) ``` diff --git a/web/book/src/reference/syntax/pipes.md b/web/book/src/reference/syntax/pipes.md index fe625bf06859..3a7cb9f1e755 100644 --- a/web/book/src/reference/syntax/pipes.md +++ b/web/book/src/reference/syntax/pipes.md @@ -66,7 +66,7 @@ from employees group {title, country} ( aggregate { average salary, - ct = count salary, + ct=(count salary), } ) ``` diff --git a/web/book/tests/documentation/snapshots/documentation__book__README__prql-language-book__1.snap b/web/book/tests/documentation/snapshots/documentation__book__README__prql-language-book__1.snap index cafe9d8c6da3..27b38388e7b8 100644 --- a/web/book/tests/documentation/snapshots/documentation__book__README__prql-language-book__1.snap +++ b/web/book/tests/documentation/snapshots/documentation__book__README__prql-language-book__1.snap @@ -1,6 +1,6 @@ --- source: web/book/tests/documentation/book.rs -expression: "from employees\nfilter start_date > @2021-01-01 # Clear date syntax\nderive { # `derive` adds columns / variables\n gross_salary = salary + (tax ?? 0), # Terse coalesce\n gross_cost = gross_salary + benefits, # Variables can use other variables\n}\nfilter gross_cost > 0\ngroup {title, country} ( # `group` runs a pipeline over each group\n aggregate { # `aggregate` reduces each group to a value\n average gross_salary,\n sum_gross_cost = sum gross_cost, # `=` sets a column name\n }\n)\nfilter sum_gross_cost > 100_000 # `filter` replaces both of SQL's `WHERE` & `HAVING`\nderive id = f\"{title}_{country}\" # F-strings like Python\nderive country_code = s\"LEFT(country, 2)\" # S-strings permit SQL as an escape hatch\nsort {sum_gross_cost, -country} # `-country` means descending order\ntake 1..20 # Range expressions (also valid as `take 20`)\n" +expression: "from employees\nfilter start_date > @2021-01-01 # Clear date syntax\nderive { # `derive` adds columns / variables\n gross_salary = salary + (tax ?? 0), # Terse coalesce\n gross_cost = gross_salary + benefits, # Variables can use other variables\n}\nfilter gross_cost > 0\ngroup {title, country} ( # `group` runs a pipeline over each group\n aggregate { # `aggregate` reduces each group to a value\n average gross_salary,\n sum_gross_cost=(sum gross_cost), # `=` sets a column name\n }\n)\nfilter sum_gross_cost > 100_000 # `filter` replaces both of SQL's `WHERE` & `HAVING`\nderive id = f\"{title}_{country}\" # F-strings like Python\nderive country_code = s\"LEFT(country, 2)\" # S-strings permit SQL as an escape hatch\nsort {sum_gross_cost, -country} # `-country` means descending order\ntake 1..20 # Range expressions (also valid as `take 20`)\n" --- WITH table_1 AS ( SELECT diff --git a/web/book/tests/documentation/snapshots/documentation__book__reference__stdlib__transforms__aggregate__examples__1.snap b/web/book/tests/documentation/snapshots/documentation__book__reference__stdlib__transforms__aggregate__examples__1.snap index d5059ccbe428..bdf7e5cf7504 100644 --- a/web/book/tests/documentation/snapshots/documentation__book__reference__stdlib__transforms__aggregate__examples__1.snap +++ b/web/book/tests/documentation/snapshots/documentation__book__reference__stdlib__transforms__aggregate__examples__1.snap @@ -1,6 +1,6 @@ --- source: web/book/tests/documentation/book.rs -expression: "from employees\ngroup {title, country} (\n aggregate {\n average salary,\n ct = count salary,\n }\n)\n" +expression: "from employees\ngroup {title, country} (\n aggregate {\n average salary,\n ct=(count salary),\n }\n)\n" --- SELECT title, diff --git a/web/book/tests/documentation/snapshots/documentation__book__reference__syntax__pipes__inner-transforms__0.snap b/web/book/tests/documentation/snapshots/documentation__book__reference__syntax__pipes__inner-transforms__0.snap index d5059ccbe428..bdf7e5cf7504 100644 --- a/web/book/tests/documentation/snapshots/documentation__book__reference__syntax__pipes__inner-transforms__0.snap +++ b/web/book/tests/documentation/snapshots/documentation__book__reference__syntax__pipes__inner-transforms__0.snap @@ -1,6 +1,6 @@ --- source: web/book/tests/documentation/book.rs -expression: "from employees\ngroup {title, country} (\n aggregate {\n average salary,\n ct = count salary,\n }\n)\n" +expression: "from employees\ngroup {title, country} (\n aggregate {\n average salary,\n ct=(count salary),\n }\n)\n" --- SELECT title, diff --git a/web/website/content/posts/2022-05-19-examples.md b/web/website/content/posts/2022-05-19-examples.md index a01b90e2bb52..148542bfdaf3 100644 --- a/web/website/content/posts/2022-05-19-examples.md +++ b/web/website/content/posts/2022-05-19-examples.md @@ -67,7 +67,7 @@ group {title, country} ( # `group` runs a pipeline over eac average gross_salary, sum gross_salary, average gross_cost, - sum_gross_cost = sum gross_cost, # `=` sets a column name. + sum_gross_cost=(sum gross_cost), # `=` sets a column name. ct = count this, } )