use std::sync::Arc;
use std::time::{Duration, Instant};
use crate::error::{DbError, DbResult};
#[cfg(feature = "metrics")]
use crate::metrics::MetricsCollector;
#[cfg(feature = "permission")]
use crate::permission::{PermissionAction, PermissionContext};
use crate::pool::db_pool::{DatabaseConnection, DbPool, DbPoolInner};
#[cfg(feature = "sql-parser")]
use crate::sql_parser::{SqlParser, is_ddl_operation};
use async_trait::async_trait;
#[cfg(not(any(feature = "permission", feature = "sql-parser")))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum PermissionAction {
Select,
}
use sea_orm::{ConnectionTrait, DatabaseTransaction, ExecResult, TransactionTrait};
pub struct Session {
connection: Option<DatabaseConnection>,
pool: Arc<DbPool>,
pool_inner: Arc<DbPoolInner>,
role: String,
last_write: Option<Instant>,
#[cfg(feature = "permission")]
permission_ctx: PermissionContext,
transaction: Option<DatabaseTransaction>,
transaction_start_time: Option<Instant>,
transaction_timeout: Duration,
#[cfg(feature = "metrics")]
metrics_collector: Option<Arc<MetricsCollector>>,
}
impl Session {
pub(crate) fn new(
connection: DatabaseConnection,
pool: Arc<DbPool>,
pool_inner: Arc<DbPoolInner>,
role: String,
) -> Self {
#[cfg(feature = "permission")]
let permission_ctx = {
let provider = pool_inner.take_permission_provider();
crate::permission::PermissionContext::with_provider_or_default(
role.clone(),
pool_inner.policy_cache.clone(),
provider,
)
};
#[cfg(feature = "metrics")]
let metrics = pool_inner.metrics_collector.clone();
let transaction_timeout = Duration::from_secs(300);
Session {
connection: Some(connection),
pool,
pool_inner,
role,
last_write: None,
#[cfg(feature = "permission")]
permission_ctx,
transaction: None,
transaction_start_time: None,
transaction_timeout,
#[cfg(feature = "metrics")]
metrics_collector: metrics,
}
}
pub fn role(&self) -> &str {
&self.role
}
#[cfg(feature = "permission")]
pub fn permission_ctx(&self) -> &PermissionContext {
&self.permission_ctx
}
pub fn mark_write(&mut self) {
self.last_write = Some(Instant::now());
}
#[cfg(feature = "permission")]
pub async fn check_permission(&self, table: &str, operation: &PermissionAction) -> Result<(), DbError> {
if self.permission_ctx.check_table_access(table, operation).await {
Ok(())
} else {
Err(DbError::Permission(format!(
"Permission denied for {} on {}",
operation, table
)))
}
}
pub fn is_in_transaction(&self) -> bool {
self.transaction.is_some()
}
pub async fn begin_transaction(&mut self) -> Result<(), DbError> {
if self.is_in_transaction() {
return Err(DbError::Transaction("Already in transaction".to_string()));
}
let start_time = Instant::now();
let conn = self
.connection
.as_ref()
.ok_or_else(|| DbError::Config("Connection not available".to_string()))?;
let transaction = conn.begin().await.map_err(|e| {
self.record_transaction_metrics("begin", start_time.elapsed(), false);
DbError::Transaction(format!("Failed to begin transaction: {}", e))
})?;
self.transaction = Some(transaction);
self.transaction_start_time = Some(Instant::now());
self.record_transaction_metrics("begin", start_time.elapsed(), true);
Ok(())
}
pub async fn begin(&mut self) -> DbResult<()> {
self.begin_transaction().await
}
pub async fn commit(&mut self) -> Result<(), DbError> {
let start_time = Instant::now();
if let Some(start_time) = self.transaction_start_time {
let elapsed = start_time.elapsed();
if elapsed > self.transaction_timeout {
tracing::warn!(
"Transaction timeout: elapsed {:?} exceeds timeout {:?}",
elapsed,
self.transaction_timeout
);
}
}
let transaction = self.transaction.take().ok_or_else(|| {
self.record_transaction_metrics("commit", start_time.elapsed(), false);
DbError::Transaction("No active transaction to commit".to_string())
})?;
transaction.commit().await.map_err(|e| {
self.record_transaction_metrics("commit", start_time.elapsed(), false);
DbError::Transaction(e.to_string())
})?;
self.transaction_start_time = None;
self.last_write = None;
self.record_transaction_metrics("commit", start_time.elapsed(), true);
Ok(())
}
pub async fn rollback(&mut self) -> Result<(), DbError> {
let start_time = Instant::now();
if !self.is_in_transaction() {
self.record_transaction_metrics("rollback", start_time.elapsed(), false);
return Err(DbError::Transaction("Not in transaction".to_string()));
}
if let Some(start_time) = self.transaction_start_time {
let elapsed = start_time.elapsed();
if elapsed > self.transaction_timeout {
tracing::warn!(
"Transaction timeout during rollback: elapsed {:?} exceeds timeout {:?}",
elapsed,
self.transaction_timeout
);
}
}
let transaction = self.transaction.take().ok_or_else(|| {
self.record_transaction_metrics("rollback", start_time.elapsed(), false);
DbError::Transaction("No active transaction to rollback".to_string())
})?;
transaction.rollback().await.map_err(|e| {
self.record_transaction_metrics("rollback", start_time.elapsed(), false);
DbError::Transaction(format!("Failed to rollback transaction: {}", e))
})?;
self.transaction_start_time = None;
self.record_transaction_metrics("rollback", start_time.elapsed(), true);
Ok(())
}
pub fn should_use_master(&self) -> bool {
if self.is_in_transaction() {
return true;
}
self.last_write
.map(|t| t.elapsed() < Duration::from_secs(5))
.unwrap_or(false)
}
pub fn connection(&mut self) -> Result<&mut DatabaseConnection, DbError> {
self.connection
.as_mut()
.ok_or_else(|| DbError::Config("Connection not available".to_string()))
}
pub async fn execute_raw(&self, sql: &str) -> DbResult<ExecResult> {
#[cfg(feature = "sql-parser")]
{
if is_ddl_operation(sql) {
return Err(DbError::Permission(
"DDL operations are not allowed in this context".to_string(),
));
}
}
#[cfg(not(feature = "sql-parser"))]
{
let sql_upper = sql.trim().to_uppercase();
if let Some(semi_colon_pos) = sql_upper.find(';') {
let after_semicolon = sql_upper[semi_colon_pos + 1..].trim();
if !after_semicolon.is_empty() {
tracing::warn!(
"Rejected SQL with multiple statements (potential SQL injection): {}",
sql
);
return Err(DbError::Permission(
"Multiple SQL statements not allowed in this context".to_string(),
));
}
}
let dangerous_patterns = [
("--", "Line comment"),
("/*", "Block comment"),
("*/", "Block comment end"),
("UNION ALL", "UNION injection"),
("UNION SELECT", "UNION injection"),
("UNION(", "UNION injection"),
("DROP DATABASE", "DROP DATABASE"),
("TRUNCATE TABLE", "TRUNCATE TABLE"),
("DELETE FROM", "DELETE injection"),
("DELETE(", "DELETE injection"),
("EXEC(", "EXEC injection"),
("EXECUTE(", "EXECUTE injection"),
(" xp_", "SQL Server extended stored procedure"),
(" sp_", "SQL Server stored procedure"),
(" xp_cmdshell", "SQL Server cmdshell"),
("INFORMATION_SCHEMA", "System table access"),
("SYSOBJECTS", "System table access (SQL Server)"),
("SYSCOLUMNS", "System table access"),
("0x", "Hex-encoded string"),
("||", "String concatenation"),
("CONCAT(", "String concatenation function"),
("BENCHMARK(", "Timing attack"),
("SLEEP(", "Timing attack"),
("PG_SLEEP", "Timing attack"),
("' OR '1'='1", "Classic SQL injection"),
("' OR 1=1", "Numeric SQL injection"),
(" OR 1=1", "Numeric SQL injection"),
("<script>", "Script injection"),
("javascript:", "JavaScript injection"),
("VARCHAR", "Type conversion injection"),
];
for (pattern, description) in &dangerous_patterns {
if sql_upper.contains(pattern) {
tracing::warn!(
"Rejected SQL containing dangerous pattern '{}' ({})",
pattern,
description
);
return Err(DbError::Permission(format!(
"SQL statement contains forbidden pattern: {} ({})",
pattern, description
)));
}
}
let escape_patterns = [("''", "Escaped single quote"), ("\\\\", "Double backslash")];
for (pattern, _) in &escape_patterns {
let count = sql_upper.matches(pattern).count();
if count > 10 {
tracing::warn!("Suspicious escape pattern count: {} ({} occurrences)", pattern, count);
return Err(DbError::Permission(
"SQL statement contains suspicious escape patterns".to_string(),
));
}
}
}
#[cfg(all(feature = "sql-parser", feature = "permission"))]
{
let parser = SqlParser::new();
if let Some((table_name, action)) = parser.parse_operation(sql) {
if table_name.is_empty() || is_invalid_table_name(&table_name) {
return Err(DbError::Permission(
"Failed to extract table name for permission checking".to_string(),
));
}
if !self.permission_ctx.check_table_access(&table_name, &action).await {
return Err(DbError::Permission(format!(
"Permission denied for {} on {}",
action, table_name
)));
}
} else {
return Err(DbError::Permission(
"Failed to parse SQL statement for permission checking".to_string(),
));
}
}
#[cfg(all(feature = "permission", not(feature = "sql-parser")))]
{
let (table_name, action) = parse_table_and_action(sql);
if table_name.is_empty() {
return Err(DbError::Permission(
"Failed to extract table name for permission checking".to_string(),
));
}
if !self.permission_ctx.check_table_access(&table_name, &action).await {
return Err(DbError::Permission(format!(
"Permission denied for {} on {}",
action, table_name
)));
}
}
if let Some(tx) = self.transaction.as_ref() {
return tx.execute_unprepared(sql).await.map_err(DbError::Connection);
}
let conn = self
.connection
.as_ref()
.ok_or_else(|| DbError::Config("Connection not available".to_string()))?;
conn.execute_unprepared(sql).await.map_err(DbError::Connection)
}
pub async fn execute_raw_ddl(&self, sql: &str) -> DbResult<ExecResult> {
if self.role != self.pool_inner.admin_role {
return Err(DbError::Permission(format!(
"DDL operations are only allowed for admin role. Current role: '{}', Admin role: '{}'",
self.role, self.pool_inner.admin_role
)));
}
#[cfg(feature = "sql-parser")]
{
let parser = SqlParser::new();
let parsed = parser
.parse_single(sql)
.map_err(|e| DbError::Permission(format!("DDL operation validation failed: {}", e)))?;
match parsed.operation_type {
crate::sql_parser::SqlOperationType::Ddl => {
tracing::info!("Executing DDL operation on table: {:?}", parsed.table_name);
}
crate::sql_parser::SqlOperationType::Dcl => {
return Err(DbError::Permission(
"DCL operations (GRANT/REVOKE) are not allowed".to_string(),
));
}
crate::sql_parser::SqlOperationType::Transaction => {
return Err(DbError::Permission(
"Transaction control statements (BEGIN/COMMIT/ROLLBACK) should use Session methods".to_string(),
));
}
crate::sql_parser::SqlOperationType::Select => {
return Err(DbError::Permission(
"SELECT queries should use execute() method, not execute_raw_ddl()".to_string(),
));
}
crate::sql_parser::SqlOperationType::Insert
| crate::sql_parser::SqlOperationType::Update
| crate::sql_parser::SqlOperationType::Delete => {
return Err(DbError::Permission(
"DML operations (INSERT/UPDATE/DELETE) should use execute() method, not execute_raw_ddl()"
.to_string(),
));
}
_ => {
return Err(DbError::Permission("Unknown SQL operation type".to_string()));
}
}
let sql_upper = sql.trim().to_uppercase();
let forbidden_patterns = ["DROP DATABASE", "DROP SCHEMA", "DROP ALL"];
for pattern in &forbidden_patterns {
if sql_upper.contains(pattern) {
return Err(DbError::Permission(format!(
"DDL operation not allowed: contains forbidden pattern '{}'",
pattern
)));
}
}
}
#[cfg(not(feature = "sql-parser"))]
{
let sql_upper = sql.trim().to_uppercase();
let forbidden_patterns = ["DROP DATABASE", "TRUNCATE TABLE", "DROP ALL", "DELETE FROM"];
for pattern in &forbidden_patterns {
if sql_upper.contains(pattern) {
return Err(DbError::Permission(format!(
"DDL operation not allowed: contains forbidden pattern '{}'",
pattern
)));
}
}
let allowed_prefixes = [
"CREATE TABLE",
"ALTER TABLE",
"DROP TABLE",
"CREATE INDEX",
"DROP INDEX",
"CREATE VIEW",
"DROP VIEW",
];
let is_allowed = allowed_prefixes.iter().any(|prefix| sql_upper.starts_with(prefix));
if !is_allowed {
return Err(DbError::Permission(format!(
"DDL operation not allowed: {}. Allowed operations: CREATE TABLE, ALTER TABLE, DROP TABLE, CREATE INDEX, DROP INDEX, CREATE VIEW, DROP VIEW",
sql_upper.split_whitespace().next().unwrap_or("UNKNOWN")
)));
}
}
let conn = self
.connection
.as_ref()
.ok_or_else(|| DbError::Config("Connection not available".to_string()))?;
conn.execute_unprepared(sql).await.map_err(DbError::Connection)
}
#[cfg(feature = "sql-parser")]
pub async fn execute_paramized(&self, sql: &str, params: Vec<sea_orm::Value>) -> DbResult<ExecResult> {
let parser = SqlParser::new();
let parsed = parser.parse_single(sql).map_err(|e| {
DbError::Permission(format!("Failed to parse SQL statement for parameterized query: {}", e))
})?;
if matches!(
parsed.operation_type,
crate::sql_parser::SqlOperationType::Ddl | crate::sql_parser::SqlOperationType::Dcl
) {
return Err(DbError::Permission(
"DDL and DCL operations should use execute_raw_ddl() or execute_raw() methods, not execute_paramized()"
.to_string(),
));
}
#[cfg(feature = "permission")]
let (table_name, action) = {
let action = match parsed.operation_type {
crate::sql_parser::SqlOperationType::Select => PermissionAction::Select,
crate::sql_parser::SqlOperationType::Insert => PermissionAction::Insert,
crate::sql_parser::SqlOperationType::Update => PermissionAction::Update,
crate::sql_parser::SqlOperationType::Delete => PermissionAction::Delete,
_ => PermissionAction::Select,
};
(parsed.table_name, action)
};
#[cfg(feature = "permission")]
{
if let Some(table_name) = &table_name {
if table_name.is_empty() || is_invalid_table_name(table_name) {
return Err(DbError::Permission(
"Failed to extract table name for permission checking".to_string(),
));
}
if !self.permission_ctx.check_table_access(table_name, &action).await {
return Err(DbError::Permission(format!(
"Permission denied for {} on {}",
action, table_name
)));
}
}
}
let conn = self
.connection
.as_ref()
.ok_or_else(|| DbError::Config("Connection not available".to_string()))?;
let mut sql_with_params = sql.to_string();
for param in params {
let param_str = match param {
sea_orm::Value::String(Some(s)) => format!("'{}'", s.replace('\'', "''")),
sea_orm::Value::String(None) => "NULL".to_string(),
sea_orm::Value::Int(Some(i)) => i.to_string(),
sea_orm::Value::Int(None) => "NULL".to_string(),
sea_orm::Value::BigInt(Some(i)) => i.to_string(),
sea_orm::Value::BigInt(None) => "NULL".to_string(),
sea_orm::Value::TinyInt(Some(i)) => i.to_string(),
sea_orm::Value::TinyInt(None) => "NULL".to_string(),
sea_orm::Value::SmallInt(Some(i)) => i.to_string(),
sea_orm::Value::SmallInt(None) => "NULL".to_string(),
sea_orm::Value::Float(Some(f)) => f.to_string(),
sea_orm::Value::Float(None) => "NULL".to_string(),
sea_orm::Value::Double(Some(f)) => f.to_string(),
sea_orm::Value::Double(None) => "NULL".to_string(),
sea_orm::Value::Bool(Some(b)) => {
if b {
"1".to_string()
} else {
"0".to_string()
}
}
sea_orm::Value::Bool(None) => "NULL".to_string(),
sea_orm::Value::Char(Some(c)) => format!("'{}'", c),
sea_orm::Value::Char(None) => "NULL".to_string(),
sea_orm::Value::Bytes(Some(b)) => {
let hex_str: String = b.iter().map(|byte| format!("{:02x}", byte)).collect();
format!("X'{}'", hex_str)
}
sea_orm::Value::Bytes(None) => "NULL".to_string(),
_ => {
return Err(DbError::Config(format!(
"Unsupported parameter type in parameterized query: {:?}",
param
)));
}
};
if let Some(pos) = sql_with_params.find('?') {
sql_with_params.replace_range(pos..pos + 1, ¶m_str);
} else {
break;
}
}
if let Some(tx) = self.transaction.as_ref() {
return tx
.execute_unprepared(&sql_with_params)
.await
.map_err(DbError::Connection);
}
conn.execute_unprepared(&sql_with_params)
.await
.map_err(DbError::Connection)
}
pub async fn execute(&mut self, sql: &str) -> DbResult<ExecResult> {
let start = Instant::now();
#[cfg(feature = "sql-parser")]
{
if is_ddl_operation(sql) {
return Err(DbError::Permission(
"DDL operations are not allowed in this context".to_string(),
));
}
}
#[cfg(all(feature = "permission", feature = "sql-parser"))]
let (table_name, action) = {
let parser = SqlParser::new();
parser.parse_operation(sql).ok_or_else(|| {
DbError::Permission("Failed to parse SQL statement for permission checking".to_string())
})?
};
#[cfg(all(feature = "permission", not(feature = "sql-parser")))]
let (table_name, action) = parse_table_and_action(sql);
#[cfg(all(not(feature = "permission"), feature = "sql-parser"))]
let action = crate::sql_parser::PermissionAction::Select;
#[cfg(not(any(feature = "permission", feature = "sql-parser")))]
let action = PermissionAction::Select;
#[cfg(feature = "permission")]
{
if table_name.is_empty() || is_invalid_table_name(&table_name) {
return Err(DbError::Permission(
"Failed to extract table name for permission checking".to_string(),
));
}
if !self.permission_ctx.check_table_access(&table_name, &action).await {
return Err(DbError::Permission(format!(
"Permission denied for {} on {}",
action, table_name
)));
}
}
let result = self.execute_raw(sql).await?;
let duration = start.elapsed();
self.record_query_metrics(&format!("{:?}", action), duration, true);
#[cfg(feature = "permission")]
{
if matches!(
action,
PermissionAction::Insert | PermissionAction::Update | PermissionAction::Delete
) {
self.mark_write();
}
}
Ok(result)
}
#[cfg(feature = "permission")]
pub async fn execute_with_operation(&mut self, sql: &str, operation: &PermissionAction) -> DbResult<ExecResult> {
let start = Instant::now();
#[cfg(feature = "sql-parser")]
{
if is_ddl_operation(sql) {
return Err(DbError::Permission(
"DDL operations are not allowed in this context".to_string(),
));
}
}
let table_name = extract_table_name(sql);
#[cfg(feature = "permission")]
{
if !table_name.is_empty() && !self.permission_ctx.check_table_access(&table_name, operation).await {
return Err(DbError::Permission(format!(
"Permission denied for {} on {}",
operation, table_name
)));
}
}
let result = self.execute_raw(sql).await?;
let duration = start.elapsed();
self.record_query_metrics(&format!("{:?}", operation), duration, true);
#[cfg(feature = "permission")]
{
if matches!(
operation,
PermissionAction::Insert | PermissionAction::Update | PermissionAction::Delete
) {
self.mark_write();
}
}
Ok(result)
}
pub async fn batch_execute(&mut self, sqls: Vec<&str>) -> DbResult<Vec<DbResult<ExecResult>>> {
let mut results = Vec::new();
for sql in sqls {
let result = self.execute(sql).await;
results.push(result);
}
Ok(results)
}
pub async fn batch_execute_in_transaction(&mut self, sqls: Vec<&str>) -> DbResult<Vec<ExecResult>> {
self.begin_transaction().await?;
let mut results = Vec::new();
let mut last_error = None;
for sql in sqls {
match self.execute_raw(sql).await {
Ok(result) => results.push(result),
Err(e) => {
last_error = Some(e);
break;
}
}
}
if let Some(error) = last_error {
if let Err(rollback_error) = self.rollback().await {
return Err(DbError::Transaction(format!(
"Batch execution failed: {}. Rollback also failed: {}",
error, rollback_error
)));
}
Err(error)
} else {
self.commit().await?;
Ok(results)
}
}
#[cfg(feature = "metrics")]
fn record_query_metrics(&self, query_type: &str, duration: Duration, success: bool) {
if let Some(metrics) = &self.metrics_collector {
metrics.record_query(query_type, duration, success, None);
}
}
#[cfg(not(feature = "metrics"))]
fn record_query_metrics(&self, _query_type: &str, _duration: Duration, _success: bool) {
}
#[cfg(feature = "metrics")]
fn record_connection_error(&self) {
if let Some(metrics) = &self.metrics_collector {
metrics.record_connection_error();
}
}
#[cfg(feature = "metrics")]
fn record_transaction_metrics(&self, operation: &str, duration: Duration, success: bool) {
if let Some(metrics) = &self.metrics_collector {
metrics.record_query(&format!("transaction_{}", operation), duration, success, None);
}
}
#[cfg(not(feature = "metrics"))]
fn record_transaction_metrics(&self, _operation: &str, _duration: Duration, _success: bool) {
}
pub async fn check_table_permission(&self, table_name: &str, operation: &str) -> DbResult<()> {
#[cfg(feature = "permission")]
{
let action = match operation {
"INSERT" => PermissionAction::Insert,
"SELECT" => PermissionAction::Select,
"UPDATE" => PermissionAction::Update,
"DELETE" => PermissionAction::Delete,
_ => return Err(DbError::Permission(format!("Unknown operation: {}", operation))),
};
if !self.permission_ctx.check_table_access(table_name, &action).await {
return Err(DbError::Permission(format!(
"Permission denied for {} on {}",
operation, table_name
)));
}
}
#[cfg(not(feature = "permission"))]
{
let _ = table_name;
let _ = operation;
}
Ok(())
}
#[cfg(feature = "metrics")]
pub fn record_metric(&self, operation: &str, table_name: &str, success: bool) {
if let Some(metrics) = &self.metrics_collector {
let bytes = Some(table_name.len() as u64);
metrics.record_query(operation, std::time::Duration::from_millis(0), success, bytes);
}
}
}
#[cfg(feature = "permission")]
fn is_invalid_table_name(table_name: &str) -> bool {
let table_name = table_name.trim();
if table_name.is_empty() {
return true;
}
for part in table_name.split('.') {
let part = part.trim();
if part.is_empty() {
return true;
}
let unquoted = part
.strip_prefix('"')
.and_then(|s| s.strip_suffix('"'))
.or_else(|| part.strip_prefix('`').and_then(|s| s.strip_suffix('`')))
.or_else(|| part.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')))
.unwrap_or(part)
.trim();
if unquoted.is_empty() {
return true;
}
}
false
}
impl Drop for Session {
fn drop(&mut self) {
if self.transaction.is_some() {
tracing::warn!("Session dropped with active transaction. Transaction will be rolled back by the database.");
if let Ok(_handle) = tokio::runtime::Handle::try_current() {
let conn = self.connection.take();
if let Some(conn) = conn {
tracing::error!(
"Cannot rollback transaction in Sync Drop. Transaction will be rolled back when connection is closed."
);
self.pool.release_connection(conn);
return;
}
}
}
if let Some(conn) = self.connection.take() {
self.pool.release_connection(conn);
}
}
}
#[cfg(feature = "permission")]
fn extract_table_name(sql: &str) -> String {
#[cfg(feature = "sql-parser")]
{
let parser = SqlParser::new();
if let Some((table_name, _)) = parser.parse_operation(sql) {
return table_name;
}
}
#[cfg(not(feature = "sql-parser"))]
{
let sql_upper = sql.to_uppercase();
if sql_upper.contains("FROM ") {
if let Some(start) = sql_upper.find("FROM ") {
let rest = &sql[start + 5..];
if let Some(end) = rest.find(|c| [' ', ',', ';', '(', ')'].contains(&c)) {
return rest[..end].trim().to_string();
} else {
return rest.trim().to_string();
}
}
}
if sql_upper.contains("INTO ") {
if let Some(start) = sql_upper.find("INTO ") {
let rest = &sql[start + 5..];
if let Some(end) = rest.find(|c| [' ', '(', ';'].contains(&c)) {
return rest[..end].trim().to_string();
} else {
return rest.trim().to_string();
}
}
}
if sql_upper.contains("UPDATE ") {
if let Some(start) = sql_upper.find("UPDATE ") {
let rest = &sql[start + 7..];
if let Some(end) = rest.find(|c| [' ', ';'].contains(&c)) {
return rest[..end].trim().to_string();
} else {
return rest.trim().to_string();
}
}
}
}
String::new()
}
#[cfg(all(feature = "permission", not(feature = "sql-parser")))]
fn parse_table_and_action(sql: &str) -> (String, PermissionAction) {
let table_name = extract_table_name(sql);
let sql_upper = sql.trim_start().to_uppercase();
let action = if sql_upper.starts_with("INSERT") {
PermissionAction::Insert
} else if sql_upper.starts_with("UPDATE") {
PermissionAction::Update
} else if sql_upper.starts_with("DELETE") {
PermissionAction::Delete
} else {
PermissionAction::Select
};
(table_name, action)
}
#[async_trait]
impl super::DatabaseSession for Session {
async fn execute(&mut self, sql: &str) -> DbResult<ExecResult> {
self.execute(sql).await
}
async fn execute_raw(&self, sql: &str) -> DbResult<ExecResult> {
self.execute_raw(sql).await
}
async fn execute_raw_ddl(&self, sql: &str) -> DbResult<ExecResult> {
self.execute_raw_ddl(sql).await
}
async fn begin_transaction(&mut self) -> DbResult<()> {
self.begin_transaction().await
}
async fn commit(&mut self) -> DbResult<()> {
self.commit().await
}
async fn rollback(&mut self) -> DbResult<()> {
self.rollback().await
}
fn role(&self) -> &str {
self.role()
}
fn is_in_transaction(&self) -> bool {
self.is_in_transaction()
}
}