Files
control/sql_executor/controllers.go
T
2026-07-15 09:41:56 +05:00

124 lines
2.7 KiB
Go

package sql_executor
import (
"database/sql"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type SQLController struct {
db *gorm.DB
}
type ExecuteSQLRequest struct {
SQL string `json:"sql" binding:"required"`
}
type ExecuteSQLResponse struct {
Type string `json:"type"`
Columns []string `json:"columns,omitempty"`
Rows [][]interface{} `json:"rows,omitempty"`
RowsAffected int64 `json:"rows_affected,omitempty"`
}
func NewSQLController(db *gorm.DB) *SQLController {
return &SQLController{db: db}
}
func (c *SQLController) Execute(ctx *gin.Context) {
var req ExecuteSQLRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
query := strings.TrimSpace(req.SQL)
if query == "" {
ctx.JSON(http.StatusBadRequest, gin.H{"error": "sql is empty"})
return
}
if isRowsQuery(query) {
response, err := c.executeRowsQuery(query)
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
ctx.JSON(http.StatusOK, response)
return
}
result := c.db.Exec(query)
if result.Error != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
return
}
ctx.JSON(http.StatusOK, ExecuteSQLResponse{
Type: "exec",
RowsAffected: result.RowsAffected,
})
}
func isRowsQuery(query string) bool {
lower := strings.ToLower(strings.TrimSpace(query))
return strings.HasPrefix(lower, "select") || strings.HasPrefix(lower, "with") || strings.HasPrefix(lower, "pragma")
}
func (c *SQLController) executeRowsQuery(query string) (ExecuteSQLResponse, error) {
rows, err := c.db.Raw(query).Rows()
if err != nil {
return ExecuteSQLResponse{}, err
}
defer rows.Close()
columns, err := rows.Columns()
if err != nil {
return ExecuteSQLResponse{}, err
}
resultRows, err := scanRows(rows, columns)
if err != nil {
return ExecuteSQLResponse{}, err
}
return ExecuteSQLResponse{
Type: "select",
Columns: columns,
Rows: resultRows,
}, nil
}
func scanRows(rows *sql.Rows, columns []string) ([][]interface{}, error) {
result := make([][]interface{}, 0)
for rows.Next() {
values := make([]interface{}, len(columns))
valuePointers := make([]interface{}, len(columns))
for i := range values {
valuePointers[i] = &values[i]
}
if err := rows.Scan(valuePointers...); err != nil {
return nil, err
}
row := make([]interface{}, len(columns))
for i, value := range values {
if bytes, ok := value.([]byte); ok {
row[i] = string(bytes)
} else {
row[i] = value
}
}
result = append(result, row)
}
if err := rows.Err(); err != nil {
return nil, err
}
return result, nil
}