use sqlparser::ast::Statement;
use crate::columns;
pub fn is_read_only(stmt: &Statement) -> bool {
matches!(
stmt,
Statement::Query(_)
| Statement::Explain { .. }
| Statement::ExplainTable { .. }
| Statement::ShowTables { .. }
| Statement::ShowColumns { .. }
| Statement::ShowVariable { .. }
| Statement::ShowVariables { .. }
| Statement::ShowCreate { .. }
| Statement::ShowDatabases { .. }
)
}
pub fn extract_tables(stmt: &Statement) -> Vec<String> {
let mut tables = Vec::new();
match stmt {
Statement::Query(query) => {
extract_tables_from_query(query, &mut tables);
}
Statement::Insert(insert) => {
tables.push(insert.table.to_string());
if let Some(ref src) = insert.source {
if let sqlparser::ast::SetExpr::Select(ref sel) = *src.body {
extract_tables_from_select(sel, &mut tables);
}
}
}
Statement::Update(update) => {
extract_tables_from_table_with_joins(&update.table, &mut tables);
}
Statement::Delete(delete) => match &delete.from {
sqlparser::ast::FromTable::WithFromKeyword(twjs) => {
for twj in twjs {
extract_tables_from_table_with_joins(twj, &mut tables);
}
}
sqlparser::ast::FromTable::WithoutKeyword(twjs) => {
for twj in twjs {
extract_tables_from_table_with_joins(twj, &mut tables);
}
}
},
Statement::CreateTable(ct) => {
tables.push(ct.name.to_string());
}
Statement::Drop { names, .. } => {
for name in names {
tables.push(name.to_string());
}
}
Statement::AlterTable(alter_table) => {
tables.push(alter_table.name.to_string());
}
_ => {}
}
tables.sort();
tables.dedup();
tables
}
fn extract_tables_from_query(query: &sqlparser::ast::Query, tables: &mut Vec<String>) {
if let sqlparser::ast::SetExpr::Select(ref sel) = *query.body {
extract_tables_from_select(sel, tables);
}
}
fn extract_tables_from_select(sel: &sqlparser::ast::Select, tables: &mut Vec<String>) {
for item in &sel.from {
extract_table_name_from_factor(&item.relation, tables);
for join in &item.joins {
extract_table_name_from_factor(&join.relation, tables);
}
}
}
fn extract_table_name_from_factor(factor: &sqlparser::ast::TableFactor, tables: &mut Vec<String>) {
match factor {
sqlparser::ast::TableFactor::Table { name, .. } => {
tables.push(name.to_string());
}
sqlparser::ast::TableFactor::Derived { subquery, .. } => {
extract_tables_from_query(subquery, tables);
}
sqlparser::ast::TableFactor::NestedJoin {
table_with_joins, ..
} => {
extract_table_name_from_factor(&table_with_joins.relation, tables);
for join in &table_with_joins.joins {
extract_table_name_from_factor(&join.relation, tables);
}
}
_ => {}
}
}
fn extract_tables_from_table_with_joins(
twj: &sqlparser::ast::TableWithJoins,
tables: &mut Vec<String>,
) {
extract_table_name_from_factor(&twj.relation, tables);
for join in &twj.joins {
extract_table_name_from_factor(&join.relation, tables);
}
}
pub fn count_params(sql: &str) -> usize {
let mut max_dollar = 0usize;
let mut question_count = 0usize;
let bytes = sql.as_bytes();
let mut i = 0;
while i < bytes.len() {
if bytes[i] == b'\'' {
i += 1;
while i < bytes.len() {
if bytes[i] == b'\'' {
if i + 1 < bytes.len() && bytes[i + 1] == b'\'' {
i += 2; } else {
break;
}
} else {
i += 1;
}
}
i += 1; } else if bytes[i] == b'$' {
i += 1;
let start = i;
while i < bytes.len() && bytes[i].is_ascii_digit() {
i += 1;
}
if i > start {
if let Ok(n) = std::str::from_utf8(&bytes[start..i])
.unwrap_or("0")
.parse::<usize>()
{
if n > max_dollar {
max_dollar = n;
}
}
}
} else if bytes[i] == b'?' {
question_count += 1;
i += 1;
} else {
i += 1;
}
}
if max_dollar > 0 {
max_dollar
} else {
question_count
}
}
pub fn normalize(sql: &str) -> String {
let mut result = String::with_capacity(sql.len());
let mut in_string = false;
let mut prev_was_space = false;
let mut chars = sql.chars().peekable();
while let Some(ch) = chars.next() {
if in_string {
result.push(ch);
if ch == '\'' {
if chars.peek() == Some(&'\'') {
result.push('\'');
chars.next();
} else {
in_string = false;
}
}
} else if ch == '\'' {
in_string = true;
prev_was_space = false;
result.push(ch);
} else if ch.is_whitespace() {
if !prev_was_space && !result.is_empty() {
result.push(' ');
prev_was_space = true;
}
} else {
prev_was_space = false;
for uc in ch.to_uppercase() {
result.push(uc);
}
}
}
if result.ends_with(' ') {
result.pop();
}
result
}
pub fn extract_columns(stmt: &Statement) -> Vec<String> {
let mut cols = Vec::new();
columns::collect_columns_from_statement(stmt, &mut cols);
cols.sort();
cols.dedup();
cols
}