diff --git a/PLAN.md b/PLAN.md index 1cdf9c8b..2c272e41 100644 --- a/PLAN.md +++ b/PLAN.md @@ -854,6 +854,28 @@ sum(expression interval) -> interval sum(expression numeric) -> numeric ``` +unnset + +```sql +select * from unnest(ARRAY[1,2], ARRAY['foo','bar','baz']); +-- ^$ hover +``` + +```sql +-- Expands multiple arrays (possibly of different data types) into a set of rows. If the arrays are not all the same length then the shorter ones are padded with NULLs. This form is only allowed in a query's FROM clause; +unnest(anyarray, anyarray [, ... ] ) -> setof anyelement, anyelement [, ... ] +``` + +```sql +select * from unnest(ARRAY[1,2]); +-- ^$ hover +``` + +```sql +-- Expands an array into a set of rows. The array's elements are read out in storage order. +unnest(anyarray) -> setof anyelement +``` + #### Column Number another example: @@ -872,6 +894,8 @@ type: string #### Star +case expr + ```sql select * from (select case -- ^$ hover @@ -886,6 +910,17 @@ select * from (select case ("case": boolean) ``` +unnest + +```sql +select * from unnest(ARRAY[1,2], ARRAY['foo','bar','baz']); +-- ^$ hover +``` + +``` +("unnest": integer, "unnest": text) +``` + ### Semantic Syntax Highlighting https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide diff --git a/crates/squawk_linter/src/rules/prefer_bigint_over_int.rs b/crates/squawk_linter/src/rules/prefer_bigint_over_int.rs index c1aae298..27c1e42a 100644 --- a/crates/squawk_linter/src/rules/prefer_bigint_over_int.rs +++ b/crates/squawk_linter/src/rules/prefer_bigint_over_int.rs @@ -33,7 +33,7 @@ fn int_to_bigint_replacement(int_type: &str) -> &'static str { fn create_bigint_fix(ty: &ast::Type) -> Option { let type_name = ty.syntax().first_token()?; let i64 = int_to_bigint_replacement(type_name.text()); - let edit = Edit::replace(ty.syntax().text_range(), i64); + let edit = Edit::replace(type_name.text_range(), i64); Some(Fix::new( format!("Replace with a 64-bit integer type: `{i64}`"), vec![edit], @@ -105,6 +105,15 @@ mod test { assert_snapshot!(fix("create table users (id serial primary key, score int not null);"), @"create table users (id bigserial primary key, score bigint not null);"); } + #[test] + fn fix_array_types() { + assert_snapshot!(fix("create table users (ids int[]);"), @"create table users (ids bigint[]);"); + assert_snapshot!(fix("create table users (ids integer[]);"), @"create table users (ids bigint[]);"); + assert_snapshot!(fix("create table users (ids int4[]);"), @"create table users (ids int8[]);"); + assert_snapshot!(fix("create table users (ids serial[]);"), @"create table users (ids bigserial[]);"); + assert_snapshot!(fix("create table users (ids serial4[]);"), @"create table users (ids serial8[]);"); + } + #[test] fn err() { let sql = r#" diff --git a/crates/squawk_linter/src/rules/prefer_bigint_over_smallint.rs b/crates/squawk_linter/src/rules/prefer_bigint_over_smallint.rs index 8555a8c8..91da67d3 100644 --- a/crates/squawk_linter/src/rules/prefer_bigint_over_smallint.rs +++ b/crates/squawk_linter/src/rules/prefer_bigint_over_smallint.rs @@ -32,7 +32,7 @@ fn smallint_to_bigint(smallint_type: &str) -> &'static str { fn create_bigint_fix(ty: &ast::Type) -> Option { let type_name = ty.syntax().first_token()?; let i64 = smallint_to_bigint(type_name.text()); - let edit = Edit::replace(ty.syntax().text_range(), i64); + let edit = Edit::replace(type_name.text_range(), i64); Some(Fix::new( format!("Replace with a 64-bit integer type: `{i64}`"), vec![edit], @@ -101,6 +101,14 @@ mod test { assert_snapshot!(fix("create table users (id smallserial primary key, score smallint not null);"), @"create table users (id bigserial primary key, score bigint not null);"); } + #[test] + fn fix_array_types() { + assert_snapshot!(fix("create table users (ids smallint[]);"), @"create table users (ids bigint[]);"); + assert_snapshot!(fix("create table users (ids int2[]);"), @"create table users (ids int8[]);"); + assert_snapshot!(fix("create table users (ids smallserial[]);"), @"create table users (ids bigserial[]);"); + assert_snapshot!(fix("create table users (ids serial2[]);"), @"create table users (ids serial8[]);"); + } + #[test] fn err() { let sql = r#" diff --git a/crates/squawk_parser/src/grammar.rs b/crates/squawk_parser/src/grammar.rs index fcb636f5..7a6502dd 100644 --- a/crates/squawk_parser/src/grammar.rs +++ b/crates/squawk_parser/src/grammar.rs @@ -2436,7 +2436,7 @@ fn expr_bp(p: &mut Parser<'_>, bp: u8, r: &Restrictions) -> Option Option { + support::token(&self.syntax, SyntaxKind::L_PAREN) + } + #[inline] + pub fn r_paren_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::R_PAREN) + } + #[inline] pub fn as_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::AS_KW) } + #[inline] + pub fn cast_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CAST_KW) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/crates/squawk_syntax/src/postgresql.ungram b/crates/squawk_syntax/src/postgresql.ungram index 95057aa6..9799a357 100644 --- a/crates/squawk_syntax/src/postgresql.ungram +++ b/crates/squawk_syntax/src/postgresql.ungram @@ -70,7 +70,9 @@ CallExpr = Expr ArgList CastExpr = - Expr (ColonColon? | 'as') Type + 'cast' '(' Expr 'as' Type ')' +| Expr ColonColon Type +| Expr Type ArrayExpr = 'array' '[' (Expr (',' Expr)*) ']'