axum_sql_viewer/api/
query.rs

1//! Raw SQL query execution endpoint
2
3use axum::{
4    extract::State,
5    http::StatusCode,
6    response::{IntoResponse, Json, Response},
7};
8use std::sync::Arc;
9
10use crate::database::traits::DatabaseProvider;
11use crate::schema::{QueryRequest, QueryResult};
12
13/// Handler for POST /api/query
14///
15/// Executes a raw SQL query and returns the results.
16///
17/// # Security Warning
18///
19/// This endpoint allows executing ANY SQL statement including INSERT, UPDATE, DELETE.
20/// It should only be used in development environments!
21///
22/// Request body:
23/// ```json
24/// {
25///   "sql": "SELECT * FROM users LIMIT 10"
26/// }
27/// ```
28///
29/// Response (successful SELECT):
30/// ```json
31/// {
32///   "columns": ["id", "name", "email"],
33///   "rows": [...],
34///   "affectedRows": 0,
35///   "executionTimeMilliseconds": 12,
36///   "error": null
37/// }
38/// ```
39///
40/// Response (successful INSERT/UPDATE/DELETE):
41/// ```json
42/// {
43///   "columns": [],
44///   "rows": [],
45///   "affectedRows": 5,
46///   "executionTimeMilliseconds": 8,
47///   "error": null
48/// }
49/// ```
50///
51/// Response (error):
52/// ```json
53/// {
54///   "columns": [],
55///   "rows": [],
56///   "affectedRows": 0,
57///   "executionTimeMilliseconds": 0,
58///   "error": "near \"SELCT\": syntax error"
59/// }
60/// ```
61///
62/// # Arguments
63///
64/// * `database` - Database provider from state
65/// * `request` - JSON request containing SQL query to execute
66///
67/// # Returns
68///
69/// JSON response containing query results or error information
70pub async fn execute_query_handler<DB: DatabaseProvider>(
71    State(database): State<Arc<DB>>,
72    Json(request): Json<QueryRequest>,
73) -> Response {
74    // Log the query execution attempt (be careful with sensitive data in production)
75    eprintln!("Executing SQL query: {}", request.sql);
76
77    match database.execute_query(&request.sql).await {
78        Ok(result) => {
79            // Check if there was an error in the result
80            if result.error.is_some() {
81                // Query execution failed, return bad request
82                (StatusCode::BAD_REQUEST, Json(result)).into_response()
83            } else {
84                // Query executed successfully
85                (StatusCode::OK, Json(result)).into_response()
86            }
87        }
88        Err(error) => {
89            eprintln!("Failed to execute query: {}", error);
90
91            // Return appropriate status code based on error type
92            let status = if error.to_string().contains("timeout") {
93                StatusCode::REQUEST_TIMEOUT
94            } else if error.to_string().contains("too large") || error.to_string().contains("TooManyRows") {
95                StatusCode::PAYLOAD_TOO_LARGE
96            } else {
97                StatusCode::BAD_REQUEST
98            };
99
100            // Return error as part of QueryResult structure
101            (
102                status,
103                Json(QueryResult {
104                    columns: vec![],
105                    rows: vec![],
106                    affected_rows: 0,
107                    execution_time_milliseconds: 0,
108                    error: Some(error.to_string()),
109                }),
110            )
111                .into_response()
112        }
113    }
114}