package database import ( "database/sql" "encoding/json" "fmt" "strings" "github.com/Azpect3120/Web-Database-Viewer/internal/templates" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" _ "github.com/lib/pq" ) func QueryCurrent(c *gin.Context) { query := c.PostForm("sql") conn := getConnection(c) if query == "" { c.String(200, templates.ErrorQueryResults(fmt.Errorf("No query provided"))) return } queries := strings.Split(query, ";") var results []string for _, query := range queries { cols, data, err := queryConnection(query, conn) if err != nil { c.String(200, templates.ErrorQueryResults(err)) return } results = append(results, templates.QueryResult(cols, data)) } c.String(200, templates.ConcatResults(results)) } func queryConnection(query, url string) ([]string, []map[string]interface{}, error) { db, err := sql.Open("postgres", url) if err != nil { return []string{}, []map[string]interface{}{}, err } defer db.Close() rows, err := db.Query(query) if err != nil { return []string{}, []map[string]interface{}{}, err } defer rows.Close() cols, err := rows.Columns() if err != nil { return []string{}, []map[string]interface{}{}, err } // Create values pointer and value pointer slices // We need to use pointers because the Scan function // requires a pointer to the value. However, there // is no simple way to create an array of pointers. values := make([]interface{}, len(cols)) valuePtrs := make([]interface{}, len(cols)) for i := range cols { valuePtrs[i] = &values[i] } // Final data structure to store the results // An array of maps, where each map is a row // and the keys are the column names. var result []map[string]interface{} for rows.Next() { // Scan the result into the value pointers err := rows.Scan(valuePtrs...) if err != nil { fmt.Println(err) } // Create a map to store the column data row := make(map[string]interface{}) for i, col := range cols { var v interface{} val := values[i] // Convert the value to a string representation // I can't see when this would fail and return // something that isn't a string, but it's better // to be safe than sorry. However, this might be // hard to handle in the frontend. b, ok := val.([]byte) if ok { v = string(b) } else { v = val } row[col] = v } // Append the row to the result set result = append(result, row) } return cols, result, nil } func getConnection(c *gin.Context) (url string) { session := sessions.Default(c) conn_bytes, ok := session.Get("connections").([]byte) curr, ok := session.Get("current").(string) if !ok { fmt.Println("No current connection") return "" } var connections map[string]string if err := json.Unmarshal(conn_bytes, &connections); err != nil { fmt.Println(err) return "" } return connections[curr] }