FEAT: Multi query support!

Queries will be split by the ';' and ran at the same time! They will
each have their own table display and info.
This commit is contained in:
Azpect3120 2024-08-16 21:04:55 -07:00
parent da3ad813be
commit 3382a1537b
6 changed files with 256 additions and 197 deletions

Binary file not shown.

View File

@ -4,6 +4,7 @@ import (
"database/sql"
"encoding/json"
"fmt"
"strings"
"github.com/Azpect3120/Web-Database-Viewer/internal/templates"
"github.com/gin-contrib/sessions"
@ -16,13 +17,19 @@ func QueryCurrent(c *gin.Context) {
query := c.PostForm("sql")
conn := getConnection(c)
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
}
c.String(200, templates.QueryResults(cols, data))
results = append(results, templates.QueryResult(cols, data))
}
c.String(200, templates.ConcatResults(results))
}
func queryConnection(query, url string) ([]string, []map[string]interface{}, error) {

View File

@ -13,7 +13,7 @@ const MANUAL_QUERY string = `
<label for="auto-toggle" class="text-sm font-medium text-gray-700">Auto-Run</label>
<input type="checkbox" name="toggle" class="toggle-checkbox">
</form>
<button hx-post="/v1/api/query" hx-trigger="click" hx-swap="outerHTML" hx-target="#query-result" hx-indicator="#spinner" hx-include="#sql" class="bg-blue-500 text-white py-2 px-3 rounded-md text-xs md:text-sm">Run Query</button>
<button hx-post="/v1/api/query" hx-trigger="click" hx-swap="outerHTML" hx-target="#query-results" hx-indicator="#spinner" hx-include="#sql" class="bg-blue-500 text-white py-2 px-3 rounded-md text-xs md:text-sm">Run Query</button>
</div>
</div>
<textarea id="sql" name="sql" rows="4" class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"></textarea>
@ -34,7 +34,7 @@ const AUTO_QUERY string = `
<button class="bg-blue-500 text-white py-2 px-3 rounded-md text-xs md:text-sm opacity-60 cursor-default" title="Disable Auto-Run to use manual queries." disabled>Run Query</button>
</div>
</div>
<textarea id="sql" name="sql" rows="4" class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" hx-post="/v1/api/query" hx-trigger="input delay:500ms" hx-swap="outerHTML" hx-target="#query-result" hx-indicator="#spinner"></textarea>
<textarea id="sql" name="sql" rows="4" class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" hx-post="/v1/api/query" hx-trigger="input delay:500ms" hx-swap="outerHTML" hx-target="#query-results" hx-indicator="#spinner"></textarea>
<p id="spinner" class="text-gray-700 text-sm px-1 py-2 htmx-indicator hidden"> Query running... </p>
<p id="query-error" hx-swap-oob="outerHTML" class="text-red-500 py-2 text-sm hidden"></p>
</div>

View File

@ -4,8 +4,19 @@ import (
"fmt"
)
// Result list definition
const result_list_open string = `<ul id="query-results">`
const result_list_close string = `</ul>`
// Result item definition
const result_item string = `
<li class="overflow-x-auto overflow-y-hidden bg-white rounded-lg shadow-md mb-8">
%s
</li>
`
// Table wrapper definitions
const table_open string = `<table id="query-result" class="min-w-full divide-y divide-gray-200">`
const table_open string = `<table class="min-w-full divide-y divide-gray-200">`
const table_close string = `</table>`
// Header definitions
@ -23,10 +34,20 @@ const query_error_message string = `<p id="query-error" hx-swap-oob="outerHTML"
const query_error_message_blank string = `<p id="query-error" hx-swap-oob="outerHTML" class="text-red-500 py-2 text-sm hidden"></p>`
func ErrorQueryResults(e error) string {
return table_open + table_close + fmt.Sprintf(query_error_message, e.Error())
return result_list_open + result_list_close + fmt.Sprintf(query_error_message, e.Error())
}
func QueryResults(cols []string, rows []map[string]interface{}) string {
func ConcatResults(items []string) string {
var html string = result_list_open
for _, h := range items {
html += h
}
return html + result_list_close
}
func QueryResult(cols []string, rows []map[string]interface{}) string {
head := generateHead(cols)
body := table_body_open
@ -36,7 +57,7 @@ func QueryResults(cols []string, rows []map[string]interface{}) string {
body += table_body_close
return table_open + head + body + table_close + query_error_message_blank
return fmt.Sprintf(result_item, table_open+head+body+table_close+query_error_message_blank)
}
// Generate the tables head row

File diff suppressed because one or more lines are too long

View File

@ -80,17 +80,13 @@
<div class="flex items-center justify-between">
<label for="sql" class="block text-sm font-medium text-gray-700">SQL Query</label>
<div class="flex items-center space-x-6">
<form class="flex items-center space-x-2" hx-get="/v1/web/query/auto" hx-swap="outerHTML"
hx-target="#query-main" hx-trigger="input">
<form class="flex items-center space-x-2" hx-get="/v1/web/query/auto" hx-swap="outerHTML" hx-target="#query-main" hx-trigger="input">
<label for="auto-toggle" class="text-sm font-medium text-gray-700">Auto-Run</label>
<input type="checkbox" name="toggle" class="toggle-checkbox"
title="Toggle auto-query functionality. Note: This will send whatever query is in the input and clear the box.">
<input type="checkbox" name="toggle" class="toggle-checkbox" title="Toggle auto-query functionality. Note: This will send whatever query is in the input and clear the box.">
</form>
<!-- Manual Query Button -->
<button hx-post="/v1/api/query" hx-trigger="click" hx-swap="outerHTML" hx-target="#query-result"
hx-indicator="#spinner" hx-include="#sql"
class="bg-blue-500 text-white py-2 px-3 rounded-md text-xs md:text-sm">Run Query</button>
<button hx-post="/v1/api/query" hx-trigger="click" hx-swap="outerHTML" hx-target="#query-results" hx-indicator="#spinner" hx-include="#sql" class="bg-blue-500 text-white py-2 px-3 rounded-md text-xs md:text-sm">Run Query</button>
<!-- Auto Query Button -->
<!-- <button class="bg-blue-500 text-white py-2 px-3 rounded-md text-xs md:text-sm opacity-60 cursor-default" title="Disable Auto-Run to use manual queries." disabled>Run Query</button> -->
@ -98,20 +94,56 @@
</div>
<!-- Manual Query Input -->
<textarea id="sql" name="sql" rows="4"
class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"></textarea>
<textarea id="sql" name="sql" rows="4" class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"></textarea>
<!-- Auto Query Input -->
<!-- <textarea id="sql" name="sql" rows="4" class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" hx-post="/v1/api/query" hx-trigger="input delay:500ms" hx-swap="outerHTML" hx-target="#query-result" hx-indicator="#spinner"></textarea> -->
<!-- <textarea id="sql" name="sql" rows="4" class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" hx-post="/v1/api/query" hx-trigger="input delay:500ms" hx-swap="outerHTML" hx-target="#query-results" hx-indicator="#spinner"></textarea> -->
<p id="spinner" class="text-gray-700 text-sm px-1 py-2 htmx-indicator hidden"> Query running... </p>
<p id="query-error" hx-swap-oob="outerHTML" class="text-red-500 py-2 text-sm hidden"></p>
</div>
<!-- Query Results -->
<div class="overflow-x-auto overflow-y-hidden bg-white shadow-md rounded-lg">
<table id="query-result" class="min-w-full divide-y divide-gray-200"></table>
</div>
<ul id="query-results">
<!-- <li class="overflow-x-auto overflow-y-hidden bg-white rounded-lg shadow-md"> -->
<!-- <table class="min-w-full divide-y divide-gray-200"> -->
<!-- <thead class="bg-gray-50"> -->
<!-- <tr> -->
<!-- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">id</th> -->
<!-- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">username</th> -->
<!-- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">password</th> -->
<!-- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">secret</th> -->
<!-- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">permission</th> -->
<!-- </tr> -->
<!-- </thead> -->
<!-- <tbody class="bg-white divide-y divide-gray-200"> -->
<!-- <tr> -->
<!-- <td class="px-6 py-4 whitespace-nowrap">513fbe25-ff69-4386-87c2-90ff6b691169</td> -->
<!-- <td class="px-6 py-4 whitespace-nowrap">ConnieH</td> -->
<!-- <td class="px-6 py-4 whitespace-nowrap">d1d15c321c523cbcfe2c18f92168cb5b:</td> -->
<!-- <td class="px-6 py-4 whitespace-nowrap"><nil></td> -->
<!-- <td class="px-6 py-4 whitespace-nowrap">Admin</td> -->
<!-- </tr> -->
<!-- <tr> -->
<!-- <td class="px-6 py-4 whitespace-nowrap">5e02c767-f035-40da-867a-1c067c1678e2</td> -->
<!-- <td class="px-6 py-4 whitespace-nowrap">AustinH</td> -->
<!-- <td class="px-6 py-4 whitespace-nowrap">588b2205579a4ee48f9014f2742fd1b0:</td> -->
<!-- <td class="px-6 py-4 whitespace-nowrap"><nil></td> -->
<!-- <td class="px-6 py-4 whitespace-nowrap">User</td> -->
<!-- </tr> -->
<!-- <tr> -->
<!-- <td class="px-6 py-4 whitespace-nowrap">1d007e19-d84b-4cc6-9e6b-1c51122aa3e5</td> -->
<!-- <td class="px-6 py-4 whitespace-nowrap">Azpect</td> -->
<!-- <td class="px-6 py-4 whitespace-nowrap">a8778398e9afed7f00a88b13ffc14a95:</td> -->
<!-- <td class="px-6 py-4 whitespace-nowrap"><nil></td> -->
<!-- <td class="px-6 py-4 whitespace-nowrap">Dev</td> -->
<!-- </tr> -->
<!-- </tbody> -->
<!-- </table> -->
<!-- <p id="query-error" hx-swap-oob="outerHTML" class="text-red-500 py-2 text-sm hidden"></p> -->
<!-- </li> -->
</ul>
</main>
</div>
</div>
@ -230,7 +262,6 @@
</div>
</div>
<!-- Manage Connections Modal -->
<div id="manager-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden opacity-0"></div>