Skip to content
Clone
main
113 lines 4.2 KB Rust
use sqlparser::dialect::PostgreSqlDialect;
use sqlparser::keywords::Keyword;
use sqlparser::tokenizer::{Token, Tokenizer};
pub fn requires_primary(sql: &str) -> bool {
let Ok(tokens) = Tokenizer::new(&PostgreSqlDialect {}, sql).tokenize() else {
return true;
};
let mut tokens = tokens
.into_iter()
.filter(|token| !matches!(token, Token::Whitespace(_)))
.peekable();
let mut previous = [Keyword::NoKeyword; 2];
let mut statement_start = true;
while let Some(token) = tokens.next() {
match token {
Token::SemiColon => {
previous = [Keyword::NoKeyword; 2];
statement_start = true;
continue;
}
Token::Word(word) => {
if word.quote_style.is_none() {
if matches!(
word.keyword,
Keyword::ABORT
| Keyword::ALTER
| Keyword::ANALYZE
| Keyword::BEGIN
| Keyword::CALL
| Keyword::CLUSTER
| Keyword::COMMENT
| Keyword::COMMIT
| Keyword::COPY
| Keyword::CREATE
| Keyword::DEALLOCATE
| Keyword::DELETE
| Keyword::DISCARD
| Keyword::DO
| Keyword::DROP
| Keyword::EXECUTE
| Keyword::GRANT
| Keyword::INSERT
| Keyword::INTO
| Keyword::LISTEN
| Keyword::LOAD
| Keyword::LOCK
| Keyword::MERGE
| Keyword::NOTIFY
| Keyword::PREPARE
| Keyword::REFRESH
| Keyword::REINDEX
| Keyword::RELEASE
| Keyword::RESET
| Keyword::REVOKE
| Keyword::ROLLBACK
| Keyword::SAVEPOINT
| Keyword::SET
| Keyword::START
| Keyword::TRUNCATE
| Keyword::UNLISTEN
| Keyword::UPDATE
| Keyword::VACUUM
) {
return true;
}
if statement_start
&& (word.keyword == Keyword::END
|| [
"checkpoint",
"reassign",
"declare",
"close",
"fetch",
"move",
]
.iter()
.any(|command| word.value.eq_ignore_ascii_case(command)))
{
return true;
}
if word.keyword == Keyword::SHARE
&& (previous[1] == Keyword::FOR || previous == [Keyword::FOR, Keyword::KEY])
{
return true;
}
}
if matches!(tokens.peek(), Some(Token::LParen)) && primary_function(&word.value) {
return true;
}
previous = [previous[1], word.keyword];
}
_ => previous = [Keyword::NoKeyword; 2],
}
statement_start = false;
}
false
}
fn primary_function(name: &str) -> bool {
let name = name.to_ascii_lowercase();
matches!(
name.as_str(),
"nextval"
| "setval"
| "set_config"
| "pg_notify"
| "pg_cancel_backend"
| "pg_terminate_backend"
) || name.starts_with("pg_advisory_")
|| name.starts_with("pg_try_advisory_")
}