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}