diff --git a/crates/squawk_ide/src/code_actions/rewrite_timestamp_type.rs b/crates/squawk_ide/src/code_actions/rewrite_timestamp_type.rs index b8dc4f35..31329c47 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_timestamp_type.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_timestamp_type.rs @@ -13,28 +13,23 @@ pub(super) fn rewrite_timestamp_type( actions: &mut Vec, ) -> Option<()> { let token = token_from_offset(db, position)?; - let time_type = token.parent_ancestors().find_map(ast::TimeType::cast)?; - - let replacement = match time_type.timezone()? { - ast::Timezone::WithoutTimezone(_) => { - if time_type.timestamp_token().is_some() { - "timestamp" - } else { - "time" - } - } - ast::Timezone::WithTimezone(_) => { - if time_type.timestamp_token().is_some() { - "timestamptz" - } else { - "timetz" - } - } + let ty = token.parent_ancestors().find_map(ast::Type::cast)?; + + let replacement = match &ty { + ast::Type::TimeType(time_type) => match time_type.timezone()? { + ast::Timezone::WithoutTimezone(_) => "time", + ast::Timezone::WithTimezone(_) => "timetz", + }, + ast::Type::TimestampType(timestamp_type) => match timestamp_type.timezone()? { + ast::Timezone::WithoutTimezone(_) => "timestamp", + ast::Timezone::WithTimezone(_) => "timestamptz", + }, + _ => return None, }; actions.push(CodeAction { title: format!("Rewrite as `{replacement}`"), - edits: vec![Edit::replace(time_type.syntax().text_range(), replacement)], + edits: vec![Edit::replace(ty.syntax().text_range(), replacement)], kind: ActionKind::RefactorRewrite, }); diff --git a/crates/squawk_ide/src/find_references.rs b/crates/squawk_ide/src/find_references.rs index 99c680d8..6612fc9f 100644 --- a/crates/squawk_ide/src/find_references.rs +++ b/crates/squawk_ide/src/find_references.rs @@ -23,11 +23,13 @@ fn is_reference_node(node: &SyntaxNode) -> bool { if let Some(ty) = ast::Type::cast(node.clone()) { return match ty { ast::Type::BitType(_) + | ast::Type::BitVaryingType(_) | ast::Type::VarcharType(_) | ast::Type::CharacterType(_) | ast::Type::DoubleType(_) | ast::Type::IntervalType(_) - | ast::Type::TimeType(_) => true, + | ast::Type::TimeType(_) + | ast::Type::TimestampType(_) => true, ast::Type::ArrayType(_) | ast::Type::ExprType(_) | ast::Type::PathType(_) diff --git a/crates/squawk_ide/src/infer.rs b/crates/squawk_ide/src/infer.rs index 4aafcff1..ad8789ef 100644 --- a/crates/squawk_ide/src/infer.rs +++ b/crates/squawk_ide/src/infer.rs @@ -60,7 +60,7 @@ pub(crate) fn infer_type_from_expr(expr: &ast::Expr) -> Option { pub(crate) fn infer_type_from_ty(ty: &ast::Type) -> Option { match ty { ast::Type::VarcharType(_) | ast::Type::CharacterType(_) => Some(Type::Text), - ast::Type::BitType(_) => Some(Type::Bit), + ast::Type::BitType(_) | ast::Type::BitVaryingType(_) => Some(Type::Bit), ast::Type::PathType(path_type) => { let name = path_type.path_ref()?.segment()?; Some(Type::Other(name.syntax().text().to_string())) diff --git a/crates/squawk_ide/src/name.rs b/crates/squawk_ide/src/name.rs index 0d079ce6..f1768590 100644 --- a/crates/squawk_ide/src/name.rs +++ b/crates/squawk_ide/src/name.rs @@ -205,14 +205,8 @@ pub(crate) fn schema_and_type_name(ty: &ast::Type) -> Option<(Option, Na let inner = array_type.ty()?; schema_and_type_name(&inner) } - ast::Type::BitType(bit_type) => { - let name = if bit_type.varying_token().is_some() { - "varbit" - } else { - "bit" - }; - Some((None, Name::from_string(name))) - } + ast::Type::BitType(_) => Some((None, Name::from_string("bit"))), + ast::Type::BitVaryingType(_) => Some((None, Name::from_string("varbit"))), ast::Type::IntervalType(_) => Some((None, Name::from_string("interval"))), ast::Type::PathType(path_type) => { let path = path_type.path_ref()?; @@ -234,14 +228,22 @@ pub(crate) fn schema_and_type_name(ty: &ast::Type) -> Option<(Option, Na ast::Type::CharacterType(_) => Some((None, Name::from_string("bpchar"))), ast::Type::DoubleType(_) => Some((None, Name::from_string("float8"))), ast::Type::TimeType(time_type) => { - let mut name = if time_type.timestamp_token().is_some() { - "timestamp".to_string() + let name = if matches!(time_type.timezone(), Some(ast::Timezone::WithTimezone(_))) { + "timetz" } else { - "time".to_string() + "time" + }; + Some((None, Name::from_string(name))) + } + ast::Type::TimestampType(timestamp_type) => { + let name = if matches!( + timestamp_type.timezone(), + Some(ast::Timezone::WithTimezone(_)) + ) { + "timestamptz" + } else { + "timestamp" }; - if let Some(ast::Timezone::WithTimezone(_)) = time_type.timezone() { - name.push_str("tz"); - } Some((None, Name::from_string(name))) } ast::Type::PercentType(_) => None, diff --git a/crates/squawk_ide/src/semantic_tokens.rs b/crates/squawk_ide/src/semantic_tokens.rs index b2e4acc3..79f7d8bc 100644 --- a/crates/squawk_ide/src/semantic_tokens.rs +++ b/crates/squawk_ide/src/semantic_tokens.rs @@ -41,6 +41,33 @@ fn highlight_param_mode(out: &mut SemanticTokenBuilder, mode: ast::ParamMode) { } } +fn highlight_timezone(out: &mut SemanticTokenBuilder, timezone: ast::Timezone) { + match timezone { + ast::Timezone::WithTimezone(with_timezone) => { + if let Some(token) = with_timezone.with_token() { + out.push_type(token.into()); + } + if let Some(token) = with_timezone.time_token() { + out.push_type(token.into()); + } + if let Some(token) = with_timezone.zone_token() { + out.push_type(token.into()); + } + } + ast::Timezone::WithoutTimezone(without_timezone) => { + if let Some(token) = without_timezone.without_token() { + out.push_type(token.into()); + } + if let Some(token) = without_timezone.time_token() { + out.push_type(token.into()); + } + if let Some(token) = without_timezone.zone_token() { + out.push_type(token.into()); + } + } + } +} + fn highlight_type(out: &mut SemanticTokenBuilder, ty: ast::Type) { match ty { ast::Type::ArrayType(_) => (), @@ -51,7 +78,15 @@ fn highlight_type(out: &mut SemanticTokenBuilder, ty: ast::Type) { if let Some(token) = bit_type.bit_token() { out.push_type(token.into()); } - if let Some(token) = bit_type.varying_token() { + } + ast::Type::BitVaryingType(bit_varying_type) => { + if let Some(token) = bit_varying_type.setof_token() { + out.push_type(token.into()); + } + if let Some(token) = bit_varying_type.bit_token() { + out.push_type(token.into()); + } + if let Some(token) = bit_varying_type.varying_token() { out.push_type(token.into()); } } @@ -119,38 +154,22 @@ fn highlight_type(out: &mut SemanticTokenBuilder, ty: ast::Type) { if let Some(token) = time_type.setof_token() { out.push_type(token.into()); } - if let Some(token) = time_type - .timestamp_token() - .or_else(|| time_type.time_token()) - { + if let Some(token) = time_type.time_token() { out.push_type(token.into()); } - if let Some(timezone) = time_type.timezone() { - match timezone { - ast::Timezone::WithTimezone(with_timezone) => { - if let Some(token) = with_timezone.with_token() { - out.push_type(token.into()); - } - if let Some(token) = with_timezone.time_token() { - out.push_type(token.into()); - } - if let Some(token) = with_timezone.zone_token() { - out.push_type(token.into()); - } - } - ast::Timezone::WithoutTimezone(without_timezone) => { - if let Some(token) = without_timezone.without_token() { - out.push_type(token.into()); - } - if let Some(token) = without_timezone.time_token() { - out.push_type(token.into()); - } - if let Some(token) = without_timezone.zone_token() { - out.push_type(token.into()); - } - } - } + highlight_timezone(out, timezone); + } + } + ast::Type::TimestampType(timestamp_type) => { + if let Some(token) = timestamp_type.setof_token() { + out.push_type(token.into()); + } + if let Some(token) = timestamp_type.timestamp_token() { + out.push_type(token.into()); + } + if let Some(timezone) = timestamp_type.timezone() { + highlight_timezone(out, timezone); } } } 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 04476306..c5694039 100644 --- a/crates/squawk_linter/src/rules/prefer_bigint_over_int.rs +++ b/crates/squawk_linter/src/rules/prefer_bigint_over_int.rs @@ -30,12 +30,14 @@ fn create_bigint_fix(ty: &ast::Type) -> Option { ast::Type::ArrayType(array_type) => return create_bigint_fix(&array_type.ty()?), ast::Type::PathType(path_type) => path_type.path_ref()?.segment()?, ast::Type::BitType(_) + | ast::Type::BitVaryingType(_) | ast::Type::VarcharType(_) | ast::Type::CharacterType(_) | ast::Type::DoubleType(_) | ast::Type::ExprType(_) | ast::Type::PercentType(_) | ast::Type::TimeType(_) + | ast::Type::TimestampType(_) | ast::Type::IntervalType(_) => return None, }; let int_type = name.text(); 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 331ce4a3..6bf4957d 100644 --- a/crates/squawk_linter/src/rules/prefer_bigint_over_smallint.rs +++ b/crates/squawk_linter/src/rules/prefer_bigint_over_smallint.rs @@ -31,12 +31,14 @@ fn create_bigint_fix(ty: &ast::Type) -> Option { ast::Type::ArrayType(array_type) => return create_bigint_fix(&array_type.ty()?), ast::Type::PathType(path_type) => path_type.path_ref()?.segment()?, ast::Type::BitType(_) + | ast::Type::BitVaryingType(_) | ast::Type::VarcharType(_) | ast::Type::CharacterType(_) | ast::Type::DoubleType(_) | ast::Type::ExprType(_) | ast::Type::PercentType(_) | ast::Type::TimeType(_) + | ast::Type::TimestampType(_) | ast::Type::IntervalType(_) => return None, }; let i64 = smallint_to_bigint(&name.text()); diff --git a/crates/squawk_linter/src/rules/prefer_identity.rs b/crates/squawk_linter/src/rules/prefer_identity.rs index e305c038..863693ab 100644 --- a/crates/squawk_linter/src/rules/prefer_identity.rs +++ b/crates/squawk_linter/src/rules/prefer_identity.rs @@ -39,12 +39,14 @@ fn create_identity_fix(ty: &ast::Type) -> Option { ast::Type::ArrayType(array_type) => return create_identity_fix(&array_type.ty()?), ast::Type::PathType(path_type) => path_type.path_ref()?.segment()?, ast::Type::BitType(_) + | ast::Type::BitVaryingType(_) | ast::Type::VarcharType(_) | ast::Type::CharacterType(_) | ast::Type::DoubleType(_) | ast::Type::ExprType(_) | ast::Type::PercentType(_) | ast::Type::TimeType(_) + | ast::Type::TimestampType(_) | ast::Type::IntervalType(_) => return None, }; let text = replace_serial(&name.text()); diff --git a/crates/squawk_linter/src/rules/prefer_text_field.rs b/crates/squawk_linter/src/rules/prefer_text_field.rs index a5a11f51..a6367066 100644 --- a/crates/squawk_linter/src/rules/prefer_text_field.rs +++ b/crates/squawk_linter/src/rules/prefer_text_field.rs @@ -30,9 +30,9 @@ fn is_not_allowed_varchar(ty: &ast::Type) -> bool { } ast::Type::VarcharType(varchar_type) => varchar_type.arg_list().is_some(), ast::Type::CharacterType(_) => false, - ast::Type::BitType(_) => false, + ast::Type::BitType(_) | ast::Type::BitVaryingType(_) => false, ast::Type::DoubleType(_) => false, - ast::Type::TimeType(_) => false, + ast::Type::TimeType(_) | ast::Type::TimestampType(_) => false, ast::Type::IntervalType(_) => false, ast::Type::ExprType(_) => false, } diff --git a/crates/squawk_linter/src/rules/prefer_timestamptz.rs b/crates/squawk_linter/src/rules/prefer_timestamptz.rs index 2f02fc04..5846bb71 100644 --- a/crates/squawk_linter/src/rules/prefer_timestamptz.rs +++ b/crates/squawk_linter/src/rules/prefer_timestamptz.rs @@ -28,16 +28,13 @@ pub fn is_not_allowed_timestamp(ty: &ast::Type) -> bool { ty_name == "varchar" && path_type.arg_list().is_some() } ast::Type::VarcharType(_) | ast::Type::CharacterType(_) => false, - ast::Type::BitType(_) => false, + ast::Type::BitType(_) | ast::Type::BitVaryingType(_) => false, ast::Type::DoubleType(_) => false, - ast::Type::TimeType(time_type) => { - if time_type.timestamp_token().is_some() - && !matches!(time_type.timezone(), Some(ast::Timezone::WithTimezone(_))) - { - return true; - } - false - } + ast::Type::TimeType(_) => false, + ast::Type::TimestampType(timestamp_type) => !matches!( + timestamp_type.timezone(), + Some(ast::Timezone::WithTimezone(_)) + ), ast::Type::IntervalType(_) => false, ast::Type::ExprType(_) => false, } @@ -45,7 +42,7 @@ pub fn is_not_allowed_timestamp(ty: &ast::Type) -> bool { fn fix_timestamp(ty: &ast::Type) -> Option { match ty { - ast::Type::TimeType(_) => { + ast::Type::TimestampType(_) => { let range = ty.syntax().text_range(); let edit = Edit::replace(range, "timestamptz"); Some(Fix::new("Replace with `timestamptz`", vec![edit])) diff --git a/crates/squawk_linter/src/visitors.rs b/crates/squawk_linter/src/visitors.rs index 21ebec14..cf7d6bd5 100644 --- a/crates/squawk_linter/src/visitors.rs +++ b/crates/squawk_linter/src/visitors.rs @@ -28,9 +28,9 @@ pub(crate) fn is_not_valid_int_type( invalid_type_names.contains(ty_name.as_str()) } ast::Type::VarcharType(_) | ast::Type::CharacterType(_) => false, - ast::Type::BitType(_) => false, + ast::Type::BitType(_) | ast::Type::BitVaryingType(_) => false, ast::Type::DoubleType(_) => false, - ast::Type::TimeType(_) => false, + ast::Type::TimeType(_) | ast::Type::TimestampType(_) => false, ast::Type::IntervalType(_) => false, ast::Type::ExprType(_) => false, } diff --git a/crates/squawk_parser/src/generated/syntax_kind.rs b/crates/squawk_parser/src/generated/syntax_kind.rs index 7dc148ad..8a781d91 100644 --- a/crates/squawk_parser/src/generated/syntax_kind.rs +++ b/crates/squawk_parser/src/generated/syntax_kind.rs @@ -690,6 +690,7 @@ pub enum SyntaxKind { BIND_PARAM_NAME_REF, BIN_EXPR, BIT_TYPE, + BIT_VARYING_TYPE, BREADTH_FIRST, BY_TARGET, CALL, @@ -1779,6 +1780,7 @@ pub enum SyntaxKind { TEXT_SEARCH_TEMPLATE_RENAME_TO, TEXT_SEARCH_TOKEN_KIND, TIES, + TIMESTAMP_TYPE, TIME_TYPE, TIME_ZONE, TIMING_AFTER, diff --git a/crates/squawk_parser/src/grammar.rs b/crates/squawk_parser/src/grammar.rs index 86096c01..dbf09bfd 100644 --- a/crates/squawk_parser/src/grammar.rs +++ b/crates/squawk_parser/src/grammar.rs @@ -2264,8 +2264,11 @@ fn opt_type_name_with(p: &mut Parser<'_>, type_args_enabled: bool) -> Option { p.bump(BIT_KW); - p.eat(VARYING_KW); - BIT_TYPE + if p.eat(VARYING_KW) { + BIT_VARYING_TYPE + } else { + BIT_TYPE + } } NATIONAL_KW if matches!(p.nth(1), CHAR_KW | CHARACTER_KW) => { p.bump(NATIONAL_KW); @@ -2273,13 +2276,18 @@ fn opt_type_name_with(p: &mut Parser<'_>, type_args_enabled: bool) -> Option char_type(p), TIMESTAMP_KW | TIME_KW => { - p.bump_any(); + let kind = if p.eat(TIMESTAMP_KW) { + TIMESTAMP_TYPE + } else { + p.bump(TIME_KW); + TIME_TYPE + }; if p.eat(L_PAREN) { expr(p); p.expect(R_PAREN); } opt_with_timezone(p); - TIME_TYPE + kind } INTERVAL_KW => { p.bump(INTERVAL_KW); @@ -2476,7 +2484,12 @@ fn name_ref_(p: &mut Parser<'_>) -> Option { let m = p.start(); let kind = match p.current() { TIMESTAMP_KW | TIME_KW => { - p.bump_any(); + let kind = if p.eat(TIMESTAMP_KW) { + TIMESTAMP_TYPE + } else { + p.bump(TIME_KW); + TIME_TYPE + }; if p.eat(L_PAREN) { if opt_numeric_literal(p).is_none() { p.error("expected numeric literal"); @@ -2484,12 +2497,15 @@ fn name_ref_(p: &mut Parser<'_>) -> Option { p.expect(R_PAREN); } opt_with_timezone(p); - TIME_TYPE + kind } BIT_KW => { p.bump(BIT_KW); - p.eat(VARYING_KW); - BIT_TYPE + if p.eat(VARYING_KW) { + BIT_VARYING_TYPE + } else { + BIT_TYPE + } } NATIONAL_KW if matches!(p.nth(1), CHAR_KW | CHARACTER_KW) => { p.bump(NATIONAL_KW); diff --git a/crates/squawk_parser/tests/snapshots/tests__comment_ok.snap b/crates/squawk_parser/tests/snapshots/tests__comment_ok.snap index 916dc090..ab9a64fe 100644 --- a/crates/squawk_parser/tests/snapshots/tests__comment_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__comment_ok.snap @@ -154,7 +154,7 @@ SOURCE_FILE BY_KW "by" WHITESPACE " " PARAM - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" R_PAREN ")" WHITESPACE " " diff --git a/crates/squawk_parser/tests/snapshots/tests__create_aggregate_ok.snap b/crates/squawk_parser/tests/snapshots/tests__create_aggregate_ok.snap index 7329a5ba..2c314cdc 100644 --- a/crates/squawk_parser/tests/snapshots/tests__create_aggregate_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__create_aggregate_ok.snap @@ -753,7 +753,7 @@ SOURCE_FILE PARAM_NAME IDENT "value2" WHITESPACE " " - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" WHITESPACE " " ORDER_KW "ORDER" diff --git a/crates/squawk_parser/tests/snapshots/tests__drop_aggregate_ok.snap b/crates/squawk_parser/tests/snapshots/tests__drop_aggregate_ok.snap index a633d527..7a96c03e 100644 --- a/crates/squawk_parser/tests/snapshots/tests__drop_aggregate_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__drop_aggregate_ok.snap @@ -157,7 +157,7 @@ SOURCE_FILE PARAM_NAME IDENT "a" WHITESPACE " " - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" COMMA "," WHITESPACE "\n " @@ -210,7 +210,7 @@ SOURCE_FILE PARAM_NAME IDENT "a" WHITESPACE " " - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" COMMA "," WHITESPACE "\n " diff --git a/crates/squawk_parser/tests/snapshots/tests__security_label_ok.snap b/crates/squawk_parser/tests/snapshots/tests__security_label_ok.snap index 3b1a2502..fa1ae946 100644 --- a/crates/squawk_parser/tests/snapshots/tests__security_label_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__security_label_ok.snap @@ -229,7 +229,7 @@ SOURCE_FILE PARAM_NAME IDENT "a" WHITESPACE " " - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" COMMA "," WHITESPACE "\n " @@ -289,7 +289,7 @@ SOURCE_FILE PARAM_NAME IDENT "a" WHITESPACE " " - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" COMMA "," WHITESPACE "\n " diff --git a/crates/squawk_parser/tests/snapshots/tests__select_casts_ok.snap b/crates/squawk_parser/tests/snapshots/tests__select_casts_ok.snap index 691cad88..d8292524 100644 --- a/crates/squawk_parser/tests/snapshots/tests__select_casts_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__select_casts_ok.snap @@ -404,7 +404,7 @@ SOURCE_FILE COLON_COLON COLON ":" COLON ":" - BIT_TYPE + BIT_VARYING_TYPE BIT_KW "bit" WHITESPACE " " VARYING_KW "varying" @@ -422,7 +422,7 @@ SOURCE_FILE COLON_COLON COLON ":" COLON ":" - BIT_TYPE + BIT_VARYING_TYPE BIT_KW "bit" WHITESPACE " " VARYING_KW "varying" @@ -665,7 +665,7 @@ SOURCE_FILE COLON_COLON COLON ":" COLON ":" - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" L_PAREN "(" LITERAL @@ -685,7 +685,7 @@ SOURCE_FILE COLON_COLON COLON ":" COLON ":" - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" L_PAREN "(" LITERAL @@ -712,7 +712,7 @@ SOURCE_FILE COLON_COLON COLON ":" COLON ":" - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" L_PAREN "(" LITERAL @@ -739,7 +739,7 @@ SOURCE_FILE COLON_COLON COLON ":" COLON ":" - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" SEMICOLON ";" WHITESPACE "\n" @@ -755,7 +755,7 @@ SOURCE_FILE COLON_COLON COLON ":" COLON ":" - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" WHITESPACE " " WITH_TIMEZONE @@ -778,7 +778,7 @@ SOURCE_FILE COLON_COLON COLON ":" COLON ":" - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" WHITESPACE " " WITHOUT_TIMEZONE @@ -934,7 +934,7 @@ SOURCE_FILE TARGET_LIST TARGET CAST_EXPR - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" WHITESPACE " " WITH_TIMEZONE @@ -958,7 +958,7 @@ SOURCE_FILE TARGET BIN_EXPR CAST_EXPR - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" WHITESPACE " " WITH_TIMEZONE @@ -992,7 +992,7 @@ SOURCE_FILE TARGET BIN_EXPR CAST_EXPR - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" WHITESPACE " " LITERAL @@ -1020,7 +1020,7 @@ SOURCE_FILE BIN_EXPR BIN_EXPR CAST_EXPR - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" WHITESPACE " " LITERAL @@ -1100,7 +1100,7 @@ SOURCE_FILE TARGET POSTFIX_EXPR CAST_EXPR - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" WHITESPACE " " WITH_TIMEZONE @@ -3107,7 +3107,7 @@ SOURCE_FILE TARGET_LIST TARGET CAST_EXPR - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" L_PAREN "(" LITERAL @@ -3125,7 +3125,7 @@ SOURCE_FILE TARGET_LIST TARGET CAST_EXPR - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" WHITESPACE " " LITERAL @@ -3636,7 +3636,7 @@ SOURCE_FILE TARGET_LIST TARGET CAST_EXPR - BIT_TYPE + BIT_VARYING_TYPE BIT_KW "bit" WHITESPACE " " VARYING_KW "varying" diff --git a/crates/squawk_parser/tests/snapshots/tests__select_funcs_ok.snap b/crates/squawk_parser/tests/snapshots/tests__select_funcs_ok.snap index 6d5e4307..6731a43c 100644 --- a/crates/squawk_parser/tests/snapshots/tests__select_funcs_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__select_funcs_ok.snap @@ -3634,7 +3634,7 @@ SOURCE_FILE FROM_KW "from" WHITESPACE " " CAST_EXPR - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" WHITESPACE " " LITERAL @@ -3658,7 +3658,7 @@ SOURCE_FILE FROM_KW "from" WHITESPACE " " CAST_EXPR - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" WHITESPACE " " LITERAL @@ -3758,7 +3758,7 @@ SOURCE_FILE FROM_KW "from" WHITESPACE " " CAST_EXPR - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" WHITESPACE " " LITERAL @@ -3782,7 +3782,7 @@ SOURCE_FILE FROM_KW "from" WHITESPACE " " CAST_EXPR - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" WHITESPACE " " LITERAL diff --git a/crates/squawk_parser/tests/snapshots/tests__select_operators_ok.snap b/crates/squawk_parser/tests/snapshots/tests__select_operators_ok.snap index aa4f76b2..1dca5d00 100644 --- a/crates/squawk_parser/tests/snapshots/tests__select_operators_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__select_operators_ok.snap @@ -4949,7 +4949,7 @@ SOURCE_FILE COLON_COLON COLON ":" COLON ":" - TIME_TYPE + TIMESTAMP_TYPE TIMESTAMP_KW "timestamp" SEMICOLON ";" WHITESPACE "\n\n" diff --git a/crates/squawk_syntax/src/ast/generated/nodes.rs b/crates/squawk_syntax/src/ast/generated/nodes.rs index e73695b8..8e27040e 100644 --- a/crates/squawk_syntax/src/ast/generated/nodes.rs +++ b/crates/squawk_syntax/src/ast/generated/nodes.rs @@ -3031,6 +3031,25 @@ impl BitType { pub fn setof_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::SETOF_KW) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct BitVaryingType { + pub(crate) syntax: SyntaxNode, +} +impl BitVaryingType { + #[inline] + pub fn arg_list(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn bit_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::BIT_KW) + } + #[inline] + pub fn setof_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::SETOF_KW) + } #[inline] pub fn varying_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::VARYING_KW) @@ -25284,10 +25303,6 @@ impl TimeType { pub fn time_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::TIME_KW) } - #[inline] - pub fn timestamp_token(&self) -> Option { - support::token(&self.syntax, SyntaxKind::TIMESTAMP_KW) - } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -25305,6 +25320,37 @@ impl TimeZone { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct TimestampType { + pub(crate) syntax: SyntaxNode, +} +impl TimestampType { + #[inline] + pub fn literal(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn timezone(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn l_paren_token(&self) -> 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 setof_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::SETOF_KW) + } + #[inline] + pub fn timestamp_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::TIMESTAMP_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TimingAfter { pub(crate) syntax: SyntaxNode, @@ -29711,6 +29757,7 @@ pub enum TrimSide { pub enum Type { ArrayType(ArrayType), BitType(BitType), + BitVaryingType(BitVaryingType), CharacterType(CharacterType), DoubleType(DoubleType), ExprType(ExprType), @@ -29718,6 +29765,7 @@ pub enum Type { PathType(PathType), PercentType(PercentType), TimeType(TimeType), + TimestampType(TimestampType), VarcharType(VarcharType), } @@ -32048,6 +32096,24 @@ impl AstNode for BitType { &self.syntax } } +impl AstNode for BitVaryingType { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::BIT_VARYING_TYPE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} impl AstNode for BreadthFirst { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -51686,6 +51752,24 @@ impl AstNode for TimeZone { &self.syntax } } +impl AstNode for TimestampType { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::TIMESTAMP_TYPE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} impl AstNode for TimingAfter { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -70051,6 +70135,7 @@ impl AstNode for Type { kind, SyntaxKind::ARRAY_TYPE | SyntaxKind::BIT_TYPE + | SyntaxKind::BIT_VARYING_TYPE | SyntaxKind::CHARACTER_TYPE | SyntaxKind::DOUBLE_TYPE | SyntaxKind::EXPR_TYPE @@ -70058,6 +70143,7 @@ impl AstNode for Type { | SyntaxKind::PATH_TYPE | SyntaxKind::PERCENT_TYPE | SyntaxKind::TIME_TYPE + | SyntaxKind::TIMESTAMP_TYPE | SyntaxKind::VARCHAR_TYPE ) } @@ -70066,6 +70152,7 @@ impl AstNode for Type { let res = match syntax.kind() { SyntaxKind::ARRAY_TYPE => Type::ArrayType(ArrayType { syntax }), SyntaxKind::BIT_TYPE => Type::BitType(BitType { syntax }), + SyntaxKind::BIT_VARYING_TYPE => Type::BitVaryingType(BitVaryingType { syntax }), SyntaxKind::CHARACTER_TYPE => Type::CharacterType(CharacterType { syntax }), SyntaxKind::DOUBLE_TYPE => Type::DoubleType(DoubleType { syntax }), SyntaxKind::EXPR_TYPE => Type::ExprType(ExprType { syntax }), @@ -70073,6 +70160,7 @@ impl AstNode for Type { SyntaxKind::PATH_TYPE => Type::PathType(PathType { syntax }), SyntaxKind::PERCENT_TYPE => Type::PercentType(PercentType { syntax }), SyntaxKind::TIME_TYPE => Type::TimeType(TimeType { syntax }), + SyntaxKind::TIMESTAMP_TYPE => Type::TimestampType(TimestampType { syntax }), SyntaxKind::VARCHAR_TYPE => Type::VarcharType(VarcharType { syntax }), _ => { return None; @@ -70085,6 +70173,7 @@ impl AstNode for Type { match self { Type::ArrayType(it) => &it.syntax, Type::BitType(it) => &it.syntax, + Type::BitVaryingType(it) => &it.syntax, Type::CharacterType(it) => &it.syntax, Type::DoubleType(it) => &it.syntax, Type::ExprType(it) => &it.syntax, @@ -70092,6 +70181,7 @@ impl AstNode for Type { Type::PathType(it) => &it.syntax, Type::PercentType(it) => &it.syntax, Type::TimeType(it) => &it.syntax, + Type::TimestampType(it) => &it.syntax, Type::VarcharType(it) => &it.syntax, } } @@ -70108,6 +70198,12 @@ impl From for Type { Type::BitType(node) } } +impl From for Type { + #[inline] + fn from(node: BitVaryingType) -> Type { + Type::BitVaryingType(node) + } +} impl From for Type { #[inline] fn from(node: CharacterType) -> Type { @@ -70150,6 +70246,12 @@ impl From for Type { Type::TimeType(node) } } +impl From for Type { + #[inline] + fn from(node: TimestampType) -> Type { + Type::TimestampType(node) + } +} impl From for Type { #[inline] fn from(node: VarcharType) -> Type { diff --git a/crates/squawk_syntax/src/column_name.rs b/crates/squawk_syntax/src/column_name.rs index 3ac07a45..0aa33ae1 100644 --- a/crates/squawk_syntax/src/column_name.rs +++ b/crates/squawk_syntax/src/column_name.rs @@ -112,16 +112,17 @@ fn name_from_type(ty: ast::Type, unknown_column: bool) -> Option<(ColumnName, Sy } } ast::Type::BitType(bit_type) => { - let name = if bit_type.varying_token().is_some() { - "varbit" - } else { - "bit" - }; return Some(( - ColumnName::new_static(name, unknown_column), + ColumnName::new_static("bit", unknown_column), bit_type.syntax().clone(), )); } + ast::Type::BitVaryingType(bit_varying_type) => { + return Some(( + ColumnName::new_static("varbit", unknown_column), + bit_varying_type.syntax().clone(), + )); + } ast::Type::VarcharType(varchar_type) => { return Some(( ColumnName::new_static("varchar", unknown_column), @@ -147,17 +148,30 @@ fn name_from_type(ty: ast::Type, unknown_column: bool) -> Option<(ColumnName, Sy )); } ast::Type::TimeType(time_type) => { - let name = match (time_type.timestamp_token().is_some(), time_type.timezone()) { - (true, Some(ast::Timezone::WithTimezone(_))) => "timestamptz", - (true, _) => "timestamp", - (false, Some(ast::Timezone::WithTimezone(_))) => "timetz", - (false, _) => "time", + let name = if matches!(time_type.timezone(), Some(ast::Timezone::WithTimezone(_))) { + "timetz" + } else { + "time" }; return Some(( ColumnName::new_static(name, unknown_column), time_type.syntax().clone(), )); } + ast::Type::TimestampType(timestamp_type) => { + let name = if matches!( + timestamp_type.timezone(), + Some(ast::Timezone::WithTimezone(_)) + ) { + "timestamptz" + } else { + "timestamp" + }; + return Some(( + ColumnName::new_static(name, unknown_column), + timestamp_type.syntax().clone(), + )); + } ast::Type::ArrayType(array_type) => { if let Some(inner_ty) = array_type.ty() { return name_from_type(inner_ty, unknown_column); diff --git a/crates/squawk_syntax/src/postgresql.ungram b/crates/squawk_syntax/src/postgresql.ungram index 5ff9fb05..fda8ea5a 100644 --- a/crates/squawk_syntax/src/postgresql.ungram +++ b/crates/squawk_syntax/src/postgresql.ungram @@ -786,7 +786,10 @@ CharacterType = ArgList? BitType = - 'setof'? 'bit' 'varying'? ArgList? + 'setof'? 'bit' ArgList? + +BitVaryingType = + 'setof'? 'bit' 'varying' ArgList? DoubleType = 'setof'? 'double' 'precision' @@ -797,7 +800,13 @@ Timezone = TimeType = 'setof'? - ('time' | 'timestamp') + 'time' + ('(' Literal ')')? + Timezone? + +TimestampType = + 'setof'? + 'timestamp' ('(' Literal ')')? Timezone? @@ -831,8 +840,10 @@ Type = | VarcharType | CharacterType | BitType +| BitVaryingType | DoubleType | TimeType +| TimestampType | IntervalType TypeName = diff --git a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__drop_aggregate_params_validation.snap b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__drop_aggregate_params_validation.snap index 61e5759d..a0194433 100644 --- a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__drop_aggregate_params_validation.snap +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__drop_aggregate_params_validation.snap @@ -144,7 +144,7 @@ SOURCE_FILE@0..389 PARAM_NAME@248..249 IDENT@248..249 "a" WHITESPACE@249..250 " " - TIME_TYPE@250..259 + TIMESTAMP_TYPE@250..259 TIMESTAMP_KW@250..259 "timestamp" COMMA@259..260 "," WHITESPACE@260..265 "\n " @@ -200,7 +200,7 @@ SOURCE_FILE@0..389 PARAM_NAME@344..345 IDENT@344..345 "a" WHITESPACE@345..346 " " - TIME_TYPE@346..355 + TIMESTAMP_TYPE@346..355 TIMESTAMP_KW@346..355 "timestamp" COMMA@355..356 "," WHITESPACE@356..361 "\n "