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
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ fn tuple_ranges(tuple: &SyntaxNode) -> Option<TupleRanges> {
mod test {
use insta::assert_snapshot;

use crate::code_actions::test_utils::{apply_code_action, code_action_not_applicable};
use crate::code_actions::test_utils::{
apply_code_action, code_action_not_applicable, code_action_not_applicable_with_errors,
};

use super::rewrite_overlaps_as_function_call;

Expand Down Expand Up @@ -126,7 +128,7 @@ mod test {

#[test]
fn not_applicable_to_non_pair_tuples() {
assert!(code_action_not_applicable(
assert!(code_action_not_applicable_with_errors(
rewrite_overlaps_as_function_call,
"select (a, b, c) over$0laps (d, e);"
));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
source: crates/squawk_syntax/src/test.rs
input_file: crates/squawk_syntax/test_data/validation/empty_in_expr.sql
---
SOURCE_FILE@0..66
SELECT@0..30
SELECT_CLAUSE@0..8
SELECT_KW@0..6 "SELECT"
WHITESPACE@6..7 " "
TARGET_LIST@7..8
TARGET@7..8
STAR@7..8 "*"
WHITESPACE@8..9 " "
FROM_CLAUSE@9..15
FROM_KW@9..13 "FROM"
WHITESPACE@13..14 " "
RELATION_FROM_ITEM@14..15
RELATION_NAME_REF@14..15
PATH_REF@14..15
PATH_SEGMENT_REF@14..15
IDENT@14..15 "t"
WHITESPACE@15..16 " "
WHERE_CLAUSE@16..29
WHERE_KW@16..21 "WHERE"
WHITESPACE@21..22 " "
BIN_EXPR@22..29
NAME_REF@22..23
IDENT@22..23 "a"
WHITESPACE@23..24 " "
IN_KW@24..26 "IN"
WHITESPACE@26..27 " "
TUPLE_EXPR@27..29
L_PAREN@27..28 "("
R_PAREN@28..29 ")"
SEMICOLON@29..30 ";"
WHITESPACE@30..31 "\n"
SELECT@31..65
SELECT_CLAUSE@31..39
SELECT_KW@31..37 "SELECT"
WHITESPACE@37..38 " "
TARGET_LIST@38..39
TARGET@38..39
STAR@38..39 "*"
WHITESPACE@39..40 " "
FROM_CLAUSE@40..46
FROM_KW@40..44 "FROM"
WHITESPACE@44..45 " "
RELATION_FROM_ITEM@45..46
RELATION_NAME_REF@45..46
PATH_REF@45..46
PATH_SEGMENT_REF@45..46
IDENT@45..46 "t"
WHITESPACE@46..47 " "
WHERE_CLAUSE@47..64
WHERE_KW@47..52 "WHERE"
WHITESPACE@52..53 " "
BIN_EXPR@53..64
NAME_REF@53..54
IDENT@53..54 "a"
WHITESPACE@54..55 " "
NOT_IN@55..61
NOT_KW@55..58 "NOT"
WHITESPACE@58..59 " "
IN_KW@59..61 "IN"
WHITESPACE@61..62 " "
TUPLE_EXPR@62..64
L_PAREN@62..63 "("
R_PAREN@63..64 ")"
SEMICOLON@64..65 ";"
WHITESPACE@65..66 "\n"

error[syntax-error]: Expected at least one expression in IN list.
╭▸
1 │ SELECT * FROM t WHERE a IN ();
╰╴ ━━
error[syntax-error]: Expected at least one expression in IN list.
╭▸
2 │ SELECT * FROM t WHERE a NOT IN ();
╰╴ ━━
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
source: crates/squawk_syntax/src/test.rs
input_file: crates/squawk_syntax/test_data/validation/overlaps.sql
---
SOURCE_FILE@0..286
COMMENT@0..61 "-- OVERLAPS is only d ..."
WHITESPACE@61..62 "\n"
SELECT@62..82
SELECT_CLAUSE@62..81
SELECT_KW@62..68 "SELECT"
WHITESPACE@68..69 " "
TARGET_LIST@69..81
TARGET@69..81
BIN_EXPR@69..81
LITERAL@69..70
INT_NUMBER@69..70 "1"
WHITESPACE@70..71 " "
OVERLAPS_KW@71..79 "OVERLAPS"
WHITESPACE@79..80 " "
LITERAL@80..81
INT_NUMBER@80..81 "2"
SEMICOLON@81..82 ";"
WHITESPACE@82..84 "\n\n"
COMMENT@84..149 "-- wrong number of pa ..."
WHITESPACE@149..150 "\n"
SELECT@150..183
SELECT_CLAUSE@150..182
SELECT_KW@150..156 "SELECT"
WHITESPACE@156..157 " "
TARGET_LIST@157..182
TARGET@157..182
BIN_EXPR@157..182
TUPLE_EXPR@157..166
L_PAREN@157..158 "("
LITERAL@158..159
INT_NUMBER@158..159 "1"
COMMA@159..160 ","
WHITESPACE@160..161 " "
LITERAL@161..162
INT_NUMBER@161..162 "2"
COMMA@162..163 ","
WHITESPACE@163..164 " "
LITERAL@164..165
INT_NUMBER@164..165 "3"
R_PAREN@165..166 ")"
WHITESPACE@166..167 " "
OVERLAPS_KW@167..175 "OVERLAPS"
WHITESPACE@175..176 " "
TUPLE_EXPR@176..182
L_PAREN@176..177 "("
LITERAL@177..178
INT_NUMBER@177..178 "1"
COMMA@178..179 ","
WHITESPACE@179..180 " "
LITERAL@180..181
INT_NUMBER@180..181 "2"
R_PAREN@181..182 ")"
SEMICOLON@182..183 ";"
WHITESPACE@183..185 "\n\n"
COMMENT@185..251 "-- wrong number of pa ..."
WHITESPACE@251..252 "\n"
SELECT@252..285
SELECT_CLAUSE@252..284
SELECT_KW@252..258 "SELECT"
WHITESPACE@258..259 " "
TARGET_LIST@259..284
TARGET@259..284
BIN_EXPR@259..284
TUPLE_EXPR@259..265
L_PAREN@259..260 "("
LITERAL@260..261
INT_NUMBER@260..261 "1"
COMMA@261..262 ","
WHITESPACE@262..263 " "
LITERAL@263..264
INT_NUMBER@263..264 "2"
R_PAREN@264..265 ")"
WHITESPACE@265..266 " "
OVERLAPS_KW@266..274 "OVERLAPS"
WHITESPACE@274..275 " "
TUPLE_EXPR@275..284
L_PAREN@275..276 "("
LITERAL@276..277
INT_NUMBER@276..277 "1"
COMMA@277..278 ","
WHITESPACE@278..279 " "
LITERAL@279..280
INT_NUMBER@279..280 "2"
COMMA@280..281 ","
WHITESPACE@281..282 " "
LITERAL@282..283
INT_NUMBER@282..283 "3"
R_PAREN@283..284 ")"
SEMICOLON@284..285 ";"
WHITESPACE@285..286 "\n"

error[syntax-error]: OVERLAPS operand must be a row expression
╭▸
2 │ SELECT 1 OVERLAPS 2;
╰╴ ━
error[syntax-error]: OVERLAPS operand must be a row expression
╭▸
2 │ SELECT 1 OVERLAPS 2;
╰╴ ━
error[syntax-error]: wrong number of parameters
╭▸
5 │ SELECT (1, 2, 3) OVERLAPS (1, 2);
╰╴ ━━━━━━━━━
error[syntax-error]: wrong number of parameters
╭▸
8 │ SELECT (1, 2) OVERLAPS (1, 2, 3);
╰╴ ━━━━━━━━━
39 changes: 32 additions & 7 deletions crates/squawk_syntax/src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,17 +590,27 @@ fn offset_range(start: TextSize, range: Range<usize>) -> TextRange {
TextRange::new(begin, end)
}

// see: https://christopher.xyz/2020/08/01/any-all-pg.html
fn validate_bin_expr(bin_expr: ast::BinExpr, acc: &mut Vec<SyntaxError>) {
if !matches!(
bin_expr.op(),
Some(ast::BinOp::In(_) | ast::BinOp::NotIn(_))
) {
return;
match bin_expr.op() {
Some(ast::BinOp::In(_) | ast::BinOp::NotIn(_)) => validate_in_expr(&bin_expr, acc),
Some(ast::BinOp::Overlaps(_)) => validate_overlaps_expr(&bin_expr, acc),
_ => (),
}
}

// see: https://christopher.xyz/2020/08/01/any-all-pg.html
fn validate_in_expr(bin_expr: &ast::BinExpr, acc: &mut Vec<SyntaxError>) {
let rhs = match bin_expr.rhs() {
None | Some(ast::Expr::ParenExpr(_)) => return,
Some(ast::Expr::TupleExpr(tuple)) if tuple.row_token().is_none() => return,
Some(ast::Expr::TupleExpr(tuple)) if tuple.row_token().is_none() => {
if tuple.exprs().next().is_none() {
acc.push(SyntaxError::new(
"Expected at least one expression in IN list.",
tuple.syntax().text_range(),
));
}
return;
}
Some(rhs) => rhs,
};
acc.push(SyntaxError::new(
Expand All @@ -609,6 +619,21 @@ fn validate_bin_expr(bin_expr: ast::BinExpr, acc: &mut Vec<SyntaxError>) {
));
}

fn validate_overlaps_expr(bin_expr: &ast::BinExpr, acc: &mut Vec<SyntaxError>) {
for operand in [bin_expr.lhs(), bin_expr.rhs()].into_iter().flatten() {
match operand {
ast::Expr::TupleExpr(tuple) if tuple.exprs().count() != 2 => acc.push(
SyntaxError::new("wrong number of parameters", tuple.syntax().text_range()),
),
ast::Expr::TupleExpr(_) => (),
operand => acc.push(SyntaxError::new(
"OVERLAPS operand must be a row expression",
operand.syntax().text_range(),
)),
}
}
}

fn validate_join_expr(join_expr: ast::JoinExpr, acc: &mut Vec<SyntaxError>) {
let Some(join) = join_expr.join() else {
return;
Expand Down
2 changes: 2 additions & 0 deletions crates/squawk_syntax/test_data/validation/empty_in_expr.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
SELECT * FROM t WHERE a IN ();
SELECT * FROM t WHERE a NOT IN ();
8 changes: 8 additions & 0 deletions crates/squawk_syntax/test_data/validation/overlaps.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- OVERLAPS is only defined over row expressions, not scalars
SELECT 1 OVERLAPS 2;

-- wrong number of parameters on left side of OVERLAPS expression
SELECT (1, 2, 3) OVERLAPS (1, 2);

-- wrong number of parameters on right side of OVERLAPS expression
SELECT (1, 2) OVERLAPS (1, 2, 3);
Loading