parent
bd490bc18f
commit
b2d5332f67
@ -0,0 +1,69 @@
|
|||||||
|
package themis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FormatRows(rows *sql.Rows) (string, error) {
|
||||||
|
sb := strings.Builder{}
|
||||||
|
|
||||||
|
cols, err := rows.Columns()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get rows columns: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := make([]string, len(cols))
|
||||||
|
for i := range c {
|
||||||
|
c[i] = " %-*s "
|
||||||
|
}
|
||||||
|
pattern := fmt.Sprintf("|%s|\n", strings.Join(c, "|"))
|
||||||
|
|
||||||
|
lengths := make([]int, len(cols))
|
||||||
|
for i := range lengths {
|
||||||
|
lengths[i] = len(cols[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
scanned := make([][]any, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
row := make([]interface{}, len(cols))
|
||||||
|
for i := range row {
|
||||||
|
row[i] = new(string)
|
||||||
|
}
|
||||||
|
rows.Scan(row...)
|
||||||
|
scanned = append(scanned, row) // keep track of row for later
|
||||||
|
for i, a := range row {
|
||||||
|
s := a.(*string)
|
||||||
|
if len(*s) > lengths[i] {
|
||||||
|
lengths[i] = len(*s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write column names
|
||||||
|
curr := make([]any, 0, 2*len(cols))
|
||||||
|
for i := range lengths {
|
||||||
|
curr = append(curr, lengths[i], cols[i])
|
||||||
|
}
|
||||||
|
sb.WriteString(fmt.Sprintf(pattern, curr...))
|
||||||
|
|
||||||
|
// Write header separator row
|
||||||
|
curr = curr[:0] // empty slice but preserve capacity
|
||||||
|
for i := range lengths {
|
||||||
|
curr = append(curr, lengths[i], strings.Repeat("-", lengths[i]))
|
||||||
|
}
|
||||||
|
sb.WriteString(fmt.Sprintf(pattern, curr...))
|
||||||
|
|
||||||
|
// iterate rows and write each one
|
||||||
|
for _, r := range scanned {
|
||||||
|
curr = curr[:0] // empty slice but preserve capacity
|
||||||
|
for i := range lengths {
|
||||||
|
s := r[i].(*string)
|
||||||
|
curr = append(curr, lengths[i], *s)
|
||||||
|
}
|
||||||
|
sb.WriteString(fmt.Sprintf(pattern, curr...))
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.String(), nil
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package themis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFormatRows(t *testing.T) {
|
||||||
|
store, err := NewStore(fmt.Sprintf(TEST_CONN_STRING_PATTERN, "format-rows"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
rows, err := store.db.Query("SELECT provinces.name, provinces.region, provinces.area, provinces.trade_node FROM provinces WHERE area = 'Gascony'")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
fmtd, err := FormatRows(rows)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, `| name | region | area | trade_node |
|
||||||
|
| -------- | ------ | ------- | ---------- |
|
||||||
|
| Labourd | France | Gascony | Bordeaux |
|
||||||
|
| Armagnac | France | Gascony | Bordeaux |
|
||||||
|
| Béarn | France | Gascony | Bordeaux |
|
||||||
|
| Foix | France | Gascony | Bordeaux |
|
||||||
|
`, fmtd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatRowsAggregated(t *testing.T) {
|
||||||
|
store, err := NewStore(fmt.Sprintf(TEST_CONN_STRING_PATTERN, "format-rows"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
rows, err := store.db.Query("SELECT count(1) as total, trade_node from provinces where region = 'France' group by trade_node")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
fmtd, err := FormatRows(rows)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, `| total | trade_node |
|
||||||
|
| ----- | --------------- |
|
||||||
|
| 25 | Bordeaux |
|
||||||
|
| 24 | Champagne |
|
||||||
|
| 8 | English Channel |
|
||||||
|
| 4 | Genoa |
|
||||||
|
| 5 | Valencia |
|
||||||
|
`, fmtd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatRowsInvalidQuery(t *testing.T) {
|
||||||
|
store, err := NewStore(fmt.Sprintf(TEST_CONN_STRING_PATTERN, "format-rows"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = store.db.Query("SELECT count(name), distinct(trade_node) from provinces where region = 'France'")
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
Loading…
Reference in new issue