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
88 changes: 88 additions & 0 deletions PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,36 @@ SELECT customer_id, merchant_id, request_id, count(*) from t
group by all;
```

and an action to go backwards:

```sql
SELECT customer_id, merchant_id, request_id, count(*) from t
group by all;
-- ^action:expand-group-by-all-names
```

becomes:

```sql
SELECT customer_id, merchant_id, request_id, count(*) from t
group by customer_id, merchant_id, request_id;
```

or with column numbers:

```sql
SELECT customer_id, merchant_id, request_id, count(*) from t
group by all;
-- ^action:expand-group-by-all-numbers
```

becomes:

```sql
SELECT customer_id, merchant_id, request_id, count(*) from t
group by 1, 2, 3;
```

### Rule: unused column

```sql
Expand Down Expand Up @@ -1004,6 +1034,64 @@ select foo, "a" from t;
select foo, 'a' from t;
```

### Quick Fix: Quote and Unquote columns, tables, etc.

```sql
select "x" from "t";
-- ^ Quick Fix: unquote
```

gives

```sql
select x from "t";
```

and vice versa:

```sql
select x from "t";
-- ^ Quick Fix: quote
```

gives:

Note: we have to be mindful of casing here,

```sql
select "x" from "t";
```

Note: there's some gotchas with this that we need to handle:

```sql
-- okay
with t("X") as (select 1)
select "X" from t;

-- err
with t("X") as (select 1)
select X from t;

-- err
with t("X") as (select 1)
select x from t;
```

or invalid column names:

```sql
-- ok
with t("a-b") as (select 1)
select "a-b" from t;

-- err
with t("a-b") as (select 1)
select a-b from t;
-- Query 1 ERROR at Line 2: ERROR: column "a" does not exist
-- LINE 2: select a-b from t;
```

### Quick Fix: in array

```sql
Expand Down
1 change: 1 addition & 0 deletions crates/squawk_parser/src/generated/syntax_kind.rs

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

8 changes: 5 additions & 3 deletions crates/squawk_parser/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2120,14 +2120,14 @@ fn index_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker {
if p.eat(COLON) {
// foo[:]
if p.eat(R_BRACK) {
return m.complete(p, INDEX_EXPR);
return m.complete(p, SLICE_EXPR);
} else {
// foo[:b]
if expr(p).is_none() {
p.error("expected an expression");
}
p.expect(R_BRACK);
return m.complete(p, INDEX_EXPR);
return m.complete(p, SLICE_EXPR);
}
}
// foo[a]
Expand All @@ -2139,12 +2139,14 @@ fn index_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker {
if p.eat(COLON) {
// foo[a:]
if p.eat(R_BRACK) {
return m.complete(p, INDEX_EXPR);
return m.complete(p, SLICE_EXPR);
}
// foo[a:b]
if expr(p).is_none() {
p.error("expected an expression");
}
p.expect(R_BRACK);
return m.complete(p, SLICE_EXPR);
}
p.expect(R_BRACK);
}
Expand Down
4 changes: 2 additions & 2 deletions crates/squawk_parser/tests/snapshots/tests__insert_ok.snap
Original file line number Diff line number Diff line change
Expand Up @@ -728,8 +728,8 @@ SOURCE_FILE
COMMA ","
WHITESPACE " "
COLUMN
INDEX_EXPR
INDEX_EXPR
SLICE_EXPR
SLICE_EXPR
NAME_REF
IDENT "board"
L_BRACK "["
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1416,8 +1416,8 @@ SOURCE_FILE
WHITESPACE " "
TARGET_LIST
TARGET
INDEX_EXPR
INDEX_EXPR
SLICE_EXPR
SLICE_EXPR
NAME_REF
IDENT "b"
L_BRACK "["
Expand All @@ -1444,8 +1444,8 @@ SOURCE_FILE
WHITESPACE " "
TARGET_LIST
TARGET
INDEX_EXPR
INDEX_EXPR
SLICE_EXPR
SLICE_EXPR
NAME_REF
IDENT "c"
L_BRACK "["
Expand All @@ -1468,8 +1468,8 @@ SOURCE_FILE
WHITESPACE " "
TARGET_LIST
TARGET
INDEX_EXPR
INDEX_EXPR
SLICE_EXPR
SLICE_EXPR
NAME_REF
IDENT "schedule"
L_BRACK "["
Expand Down Expand Up @@ -3931,7 +3931,7 @@ SOURCE_FILE
WHITESPACE " "
TARGET_LIST
TARGET
INDEX_EXPR
SLICE_EXPR
LITERAL
POSITIONAL_PARAM "$1"
L_BRACK "["
Expand Down
47 changes: 47 additions & 0 deletions crates/squawk_syntax/src/ast/generated/nodes.rs

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

28 changes: 28 additions & 0 deletions crates/squawk_syntax/src/ast/node_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,34 @@ impl ast::IndexExpr {
}
}

impl ast::SliceExpr {
#[inline]
pub fn base(&self) -> Option<ast::Expr> {
support::children(&self.syntax).next()
}

#[inline]
pub fn start(&self) -> Option<ast::Expr> {
// With `select x[1:]`, we have two exprs, `x` and `1`.
// We skip over the first one, and then we want the second one, but we
// want to make sure we don't choose the end expr if instead we had:
// `select x[:1]`
let colon = self.colon_token()?;
support::children(&self.syntax)
.skip(1)
.find(|expr: &ast::Expr| expr.syntax().text_range().end() <= colon.text_range().start())
}

#[inline]
pub fn end(&self) -> Option<ast::Expr> {
// We want to make sure we get the last expr after the `:` which is the
// end of the slice, i.e., `2` in: `select x[:2]`
let colon = self.colon_token()?;
support::children(&self.syntax)
.find(|expr: &ast::Expr| expr.syntax().text_range().start() >= colon.text_range().end())
}
}

impl ast::BetweenExpr {
#[inline]
pub fn target(&self) -> Option<ast::Expr> {
Expand Down
49 changes: 49 additions & 0 deletions crates/squawk_syntax/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,55 @@ fn index_expr() {
assert_eq!(index.syntax().text(), "bar");
}

#[test]
fn slice_expr() {
use insta::assert_snapshot;
let source_code = "
select x[1:2], x[2:], x[:3], x[:];
";
let parse = SourceFile::parse(source_code);
assert!(parse.errors().is_empty());
let file: SourceFile = parse.tree();
let stmt = file.stmts().next().unwrap();
let ast::Stmt::Select(select) = stmt else {
unreachable!()
};
let select_clause = select.select_clause().unwrap();
let mut targets = select_clause.target_list().unwrap().targets();

let ast::Expr::SliceExpr(slice) = targets.next().unwrap().expr().unwrap() else {
unreachable!()
};
assert_snapshot!(slice.syntax(), @"x[1:2]");
assert_eq!(slice.base().unwrap().syntax().text(), "x");
assert_eq!(slice.start().unwrap().syntax().text(), "1");
assert_eq!(slice.end().unwrap().syntax().text(), "2");

let ast::Expr::SliceExpr(slice) = targets.next().unwrap().expr().unwrap() else {
unreachable!()
};
assert_snapshot!(slice.syntax(), @"x[2:]");
assert_eq!(slice.base().unwrap().syntax().text(), "x");
assert_eq!(slice.start().unwrap().syntax().text(), "2");
assert!(slice.end().is_none());

let ast::Expr::SliceExpr(slice) = targets.next().unwrap().expr().unwrap() else {
unreachable!()
};
assert_snapshot!(slice.syntax(), @"x[:3]");
assert_eq!(slice.base().unwrap().syntax().text(), "x");
assert!(slice.start().is_none());
assert_eq!(slice.end().unwrap().syntax().text(), "3");

let ast::Expr::SliceExpr(slice) = targets.next().unwrap().expr().unwrap() else {
unreachable!()
};
assert_snapshot!(slice.syntax(), @"x[:]");
assert_eq!(slice.base().unwrap().syntax().text(), "x");
assert!(slice.start().is_none());
assert!(slice.end().is_none());
}

#[test]
fn field_expr() {
let source_code = "
Expand Down
Loading
Loading