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
27 changes: 15 additions & 12 deletions crates/squawk_ide/src/binder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1740,7 +1740,7 @@ fn bind_release_savepoint(b: &mut Binder, release: ast::ReleaseSavepoint) {
}

fn bind_commit(b: &mut Binder, commit: ast::Commit) {
if commit.prepared_token().is_some() {
if let ast::Commit::CommitPrepared(commit) = commit {
bind_prepared_transaction_ref(b, commit.literal());
}

Expand Down Expand Up @@ -1778,17 +1778,20 @@ fn bind_prepared_transaction_ref(b: &mut Binder, literal: Option<ast::Literal>)
}

fn bind_rollback(b: &mut Binder, rollback: ast::Rollback) {
if rollback.prepared_token().is_some() {
bind_prepared_transaction_ref(b, rollback.literal());
}

let Some(savepoint_ref) = rollback.savepoint_ref() else {
b.savepoint_stack.clear();
return;
};

if let Some(idx) = bind_savepoint_ref(b, &savepoint_ref) {
b.savepoint_stack.truncate(idx + 1);
match rollback {
ast::Rollback::RollbackPrepared(rollback) => {
bind_prepared_transaction_ref(b, rollback.literal());
b.savepoint_stack.clear();
}
ast::Rollback::RollbackToSavepoint(rollback) => {
let Some(savepoint_ref) = rollback.savepoint_ref() else {
return;
};
if let Some(idx) = bind_savepoint_ref(b, &savepoint_ref) {
b.savepoint_stack.truncate(idx + 1);
}
}
ast::Rollback::RollbackTransaction(_) => b.savepoint_stack.clear(),
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/squawk_ide/src/classify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,9 +425,9 @@ fn is_prepared_transaction_id(node: &SyntaxNode) -> bool {
return false;
};
if let Some(commit) = ast::Commit::cast(parent.clone()) {
commit.prepared_token().is_some()
matches!(commit, ast::Commit::CommitPrepared(_))
} else if let Some(rollback) = ast::Rollback::cast(parent) {
rollback.prepared_token().is_some()
matches!(rollback, ast::Rollback::RollbackPrepared(_))
} else {
false
}
Expand Down
3 changes: 2 additions & 1 deletion crates/squawk_ide/src/find_references.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ fn is_reference_node(node: &SyntaxNode) -> bool {
if let Some(ty) = ast::Type::cast(node.clone()) {
return match ty {
ast::Type::BitType(_)
| ast::Type::CharType(_)
| ast::Type::VarcharType(_)
| ast::Type::CharacterType(_)
| ast::Type::DoubleType(_)
| ast::Type::IntervalType(_)
| ast::Type::TimeType(_) => true,
Expand Down
2 changes: 1 addition & 1 deletion crates/squawk_ide/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub(crate) fn infer_type_from_expr(expr: &ast::Expr) -> Option<Type> {

pub(crate) fn infer_type_from_ty(ty: &ast::Type) -> Option<Type> {
match ty {
ast::Type::CharType(_) => Some(Type::Text),
ast::Type::VarcharType(_) | ast::Type::CharacterType(_) => Some(Type::Text),
ast::Type::BitType(_) => Some(Type::Bit),
ast::Type::PathType(path_type) => {
let name = path_type.path_ref()?.segment()?;
Expand Down
11 changes: 2 additions & 9 deletions crates/squawk_ide/src/name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,15 +230,8 @@ pub(crate) fn schema_and_type_name(ty: &ast::Type) -> Option<(Option<Schema>, Na
None
}
}
ast::Type::CharType(char_type) => {
let name = if char_type.varchar_token().is_some() || char_type.varying_token().is_some()
{
"varchar"
} else {
"bpchar"
};
Some((None, Name::from_string(name)))
}
ast::Type::VarcharType(_) => Some((None, Name::from_string("varchar"))),
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() {
Expand Down
32 changes: 23 additions & 9 deletions crates/squawk_ide/src/semantic_tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,23 +55,37 @@ fn highlight_type(out: &mut SemanticTokenBuilder, ty: ast::Type) {
out.push_type(token.into());
}
}
ast::Type::CharType(char_type) => {
if let Some(token) = char_type.setof_token() {
ast::Type::VarcharType(varchar_type) => {
if let Some(token) = varchar_type.setof_token() {
out.push_type(token.into());
}
if let Some(token) = char_type.national_token() {
if let Some(token) = varchar_type.national_token() {
out.push_type(token.into());
}

if let Some(token) = char_type
if let Some(token) = varchar_type
.varchar_token()
.or_else(|| char_type.nchar_token())
.or_else(|| char_type.character_token())
.or_else(|| char_type.char_token())
.or_else(|| varchar_type.nchar_token())
.or_else(|| varchar_type.character_token())
.or_else(|| varchar_type.char_token())
{
out.push_type(token.into());
}
if let Some(token) = char_type.varying_token() {
if let Some(token) = varchar_type.varying_token() {
out.push_type(token.into());
}
}
ast::Type::CharacterType(character_type) => {
if let Some(token) = character_type.setof_token() {
out.push_type(token.into());
}
if let Some(token) = character_type.national_token() {
out.push_type(token.into());
}
if let Some(token) = character_type
.nchar_token()
.or_else(|| character_type.character_token())
.or_else(|| character_type.char_token())
{
out.push_type(token.into());
}
}
Expand Down
30 changes: 16 additions & 14 deletions crates/squawk_linter/src/rules/ban_char_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,23 @@ fn check_path_type(ctx: &mut Linter, path_type: ast::PathType) {
}
}

fn check_char_type(ctx: &mut Linter, char_type: ast::CharType) {
if is_char_type(&char_type.text()) {
let fix = create_fix(char_type.syntax().text_range(), char_type.arg_list());
ctx.report(Violation::for_node(
Rule::BanCharField,
"Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.".into(),
char_type.syntax(),
).fix(fix));
}
fn check_character_type(ctx: &mut Linter, character_type: ast::CharacterType) {
let fix = create_fix(
character_type.syntax().text_range(),
character_type.arg_list(),
);
ctx.report(Violation::for_node(
Rule::BanCharField,
"Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.".into(),
character_type.syntax(),
).fix(fix));
}

fn check_ty(ctx: &mut Linter, ty: Option<ast::Type>) {
match ty {
Some(ast::Type::ArrayType(array_type)) => match array_type.ty() {
Some(ast::Type::CharType(char_type)) => {
check_char_type(ctx, char_type);
Some(ast::Type::CharacterType(character_type)) => {
check_character_type(ctx, character_type);
}
Some(ast::Type::PathType(path_type)) => {
check_path_type(ctx, path_type);
Expand All @@ -69,8 +70,8 @@ fn check_ty(ctx: &mut Linter, ty: Option<ast::Type>) {
Some(ast::Type::PathType(path_type)) => {
check_path_type(ctx, path_type);
}
Some(ast::Type::CharType(char_type)) => {
check_char_type(ctx, char_type);
Some(ast::Type::CharacterType(character_type)) => {
check_character_type(ctx, character_type);
}
_ => (),
}
Expand Down Expand Up @@ -180,7 +181,8 @@ create table t (
l bit varying,
m int array[],
o pg_catalog.char,
p char[]
p char[],
q nchar
);
"#;
assert_snapshot!(lint_errors(sql, Rule::BanCharField));
Expand Down
3 changes: 2 additions & 1 deletion crates/squawk_linter/src/rules/prefer_bigint_over_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ fn create_bigint_fix(ty: &ast::Type) -> Option<Fix> {
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::CharType(_)
| ast::Type::VarcharType(_)
| ast::Type::CharacterType(_)
| ast::Type::DoubleType(_)
| ast::Type::ExprType(_)
| ast::Type::PercentType(_)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ fn create_bigint_fix(ty: &ast::Type) -> Option<Fix> {
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::CharType(_)
| ast::Type::VarcharType(_)
| ast::Type::CharacterType(_)
| ast::Type::DoubleType(_)
| ast::Type::ExprType(_)
| ast::Type::PercentType(_)
Expand Down
3 changes: 2 additions & 1 deletion crates/squawk_linter/src/rules/prefer_identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ fn create_identity_fix(ty: &ast::Type) -> Option<Fix> {
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::CharType(_)
| ast::Type::VarcharType(_)
| ast::Type::CharacterType(_)
| ast::Type::DoubleType(_)
| ast::Type::ExprType(_)
| ast::Type::PercentType(_)
Expand Down
11 changes: 5 additions & 6 deletions crates/squawk_linter/src/rules/prefer_text_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ fn is_not_allowed_varchar(ty: &ast::Type) -> bool {
// if we don't have any args, then it's the same as `text`
ty_name == "varchar" && path_type.arg_list().is_some()
}
ast::Type::CharType(char_type) => {
char_type.text().eq_ignore_ascii_case("varchar") && char_type.arg_list().is_some()
}
ast::Type::VarcharType(varchar_type) => varchar_type.arg_list().is_some(),
ast::Type::CharacterType(_) => false,
ast::Type::BitType(_) => false,
ast::Type::DoubleType(_) => false,
ast::Type::TimeType(_) => false,
Expand All @@ -46,10 +45,10 @@ fn create_varchar_to_text_fix(ty: &ast::Type) -> Option<Fix> {
// so: `"varchar"(100)` becomes `text`
path_type.syntax().text_range()
}
ast::Type::CharType(char_type) => {
// we'll replace the entire char type, including args
ast::Type::VarcharType(varchar_type) => {
// we'll replace the entire varchar type, including args
// so: `varchar(100)` becomes `text`
char_type.syntax().text_range()
varchar_type.syntax().text_range()
}
ast::Type::ArrayType(array_type) => {
let ty = array_type.ty()?;
Expand Down
2 changes: 1 addition & 1 deletion crates/squawk_linter/src/rules/prefer_timestamptz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub fn is_not_allowed_timestamp(ty: &ast::Type) -> bool {
// if we don't have any args, then it's the same as `text`
ty_name == "varchar" && path_type.arg_list().is_some()
}
ast::Type::CharType(_) => false,
ast::Type::VarcharType(_) | ast::Type::CharacterType(_) => false,
ast::Type::BitType(_) => false,
ast::Type::DoubleType(_) => false,
ast::Type::TimeType(time_type) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,17 @@ warning[ban-char-field]: Using `character` is likely a mistake and should almost
╰╴
warning[ban-char-field]: Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.
╭▸
16 │ p char[]
16 │ p char[],
│ ━━━━
╭╴
16 - p char[]
16 + p text[]
16 - p char[],
16 + p text[],
╰╴
warning[ban-char-field]: Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.
╭▸
17 │ q nchar
│ ━━━━━
╭╴
17 - q nchar
17 + q text
╰╴
2 changes: 1 addition & 1 deletion crates/squawk_linter/src/visitors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub(crate) fn is_not_valid_int_type(
};
invalid_type_names.contains(ty_name.as_str())
}
ast::Type::CharType(_) => false,
ast::Type::VarcharType(_) | ast::Type::CharacterType(_) => false,
ast::Type::BitType(_) => false,
ast::Type::DoubleType(_) => false,
ast::Type::TimeType(_) => false,
Expand Down
10 changes: 7 additions & 3 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.

29 changes: 18 additions & 11 deletions crates/squawk_parser/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2208,11 +2208,14 @@ fn type_mods(
fn char_type(p: &mut Parser<'_>) -> SyntaxKind {
assert!(p.at(CHARACTER_KW) || p.at(CHAR_KW) || p.at(NCHAR_KW) || p.at(VARCHAR_KW));
if p.eat(VARCHAR_KW) {
return CHAR_TYPE;
return VARCHAR_TYPE;
}
p.bump_any();
p.eat(VARYING_KW);
CHAR_TYPE
if p.eat(VARYING_KW) {
VARCHAR_TYPE
} else {
CHARACTER_TYPE
}
}

const TYPE_NAME_FIRST: TokenSet = TokenSet::new(&[
Expand Down Expand Up @@ -7258,15 +7261,17 @@ fn commit(p: &mut Parser<'_>) -> CompletedMarker {
let is_commit = p.at(COMMIT_KW);
p.bump_any();
// PREPARED transaction_id
if is_commit && p.eat(PREPARED_KW) {
let kind = if is_commit && p.eat(PREPARED_KW) {
string_literal(p);
COMMIT_PREPARED
} else {
// [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ]
let _ = p.eat(WORK_KW) || p.eat(TRANSACTION_KW);
opt_chain_clause(p);
}
COMMIT_TRANSACTION
};
p.eat(SEMICOLON);
m.complete(p, COMMIT)
m.complete(p, kind)
}

fn opt_chain_clause(p: &mut Parser<'_>) {
Expand Down Expand Up @@ -7465,20 +7470,22 @@ fn rollback(p: &mut Parser<'_>) -> CompletedMarker {
let m = p.start();
let is_rollback = p.at(ROLLBACK_KW);
p.bump_any();
if p.eat(PREPARED_KW) {
if is_rollback && p.eat(PREPARED_KW) {
string_literal(p);
p.eat(SEMICOLON);
return m.complete(p, ROLLBACK);
return m.complete(p, ROLLBACK_PREPARED);
}
let _ = p.eat(WORK_KW) || p.eat(TRANSACTION_KW);
if is_rollback && p.eat(TO_KW) {
let kind = if is_rollback && p.eat(TO_KW) {
p.eat(SAVEPOINT_KW);
savepoint_ref(p);
ROLLBACK_TO_SAVEPOINT
} else {
opt_chain_clause(p);
}
ROLLBACK_TRANSACTION
};
p.eat(SEMICOLON);
m.complete(p, ROLLBACK)
m.complete(p, kind)
}

#[derive(Default)]
Expand Down
Loading
Loading