-
Notifications
You must be signed in to change notification settings - Fork 0
/
tabledump.go
142 lines (128 loc) · 3.28 KB
/
tabledump.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package sqllogging
import (
"context"
"database/sql"
"fmt"
"github.com/sirupsen/logrus"
"io"
"text/tabwriter"
"time"
"github.com/alecthomas/repr"
)
type Row []any
func scanRowsOfAny(rows *sql.Rows, next func(Row) error) error {
types, err := rows.ColumnTypes()
if err != nil {
return err
}
n := len(types)
rowValues := make([]interface{}, n, n)
pointers := make([]interface{}, n, n)
for i := 0; i < len(types); i++ {
pointers[i] = &rowValues[i]
}
for rows.Next() {
err = rows.Scan(pointers...)
if err != nil {
return err
}
var row Row
for i, _ := range types {
switch v := rowValues[i].(type) {
case []uint8:
row = append(row, string(v))
case int64:
// we don't really know that the query column is int64, that's just how all ints are returned,
// and it's usually more convenient in tests with int
row = append(row, int(v))
case time.Time:
row = append(row, v)
default:
row = append(row, v)
}
}
err = next(row)
if err != nil {
return err
}
}
return rows.Err()
}
func sqlQueryLogTable(tablename string) string {
return "select top(1000) * from " + sqlQuotename(tablename) + " order by 1"
}
// Dump contents of table to stream in human-readable column form
func tableDumpPrettyPrint(ctx context.Context, w io.Writer, dbi QuerierExecer, tablename string) {
_, _ = fmt.Fprintln(w, "================================")
_, _ = fmt.Fprintln(w, tablename)
_, _ = fmt.Fprintln(w, "================================")
rows, err := dbi.QueryContext(ctx, sqlQueryLogTable(tablename))
if err != nil {
_, _ = fmt.Fprintln(w, err.Error())
return
}
defer func() {
err = rows.Close()
if err != nil {
_, _ = fmt.Fprintln(w, err.Error())
}
return
}()
tw := tabwriter.NewWriter(w, 0, 0, 4, ' ', 0)
columns, err := rows.Columns()
if err != nil {
_, _ = fmt.Fprintln(w, err.Error())
return
}
err = scanRowsOfAny(rows, func(row Row) error {
for i, value := range row {
var val interface{}
switch v := value.(type) {
case string:
val = repr.String(v)
default:
val = v
}
_, _ = fmt.Fprintln(tw, fmt.Sprintf("%s\t%v\t", columns[i], val))
}
_, _ = fmt.Fprintln(tw, "----------------\t------------\t")
return nil
})
if err != nil {
_, _ = fmt.Fprintln(w, err.Error())
return
}
_ = tw.Flush()
}
// Dump contents of table to logger
func tableDumpStructured(ctx context.Context, logger logrus.FieldLogger, level logrus.Level, dbi QuerierExecer, tablename string) {
qry := sqlQueryLogTable(tablename)
rows, err := dbi.QueryContext(ctx, qry)
if err != nil {
logger.Warning("Unable to log table " + tablename + ": " + err.Error())
return
}
defer func() {
err = rows.Close()
if err != nil {
logger.Warning("Problem in rows.Close() when logging table " + tablename + ": " + err.Error())
}
return
}()
columns, err := rows.Columns()
if err != nil {
logger.Warning("Problem in rows.Columns() when logging table " + tablename + ": " + err.Error())
return
}
err = scanRowsOfAny(rows, func(row Row) error {
fields := make(logrus.Fields)
for i, value := range row {
fields[columns[i]] = value
}
logAtLevel(logger.WithFields(fields), level, "")
return nil
})
if err != nil {
logger.Warning("Problem in scanning rows when logging table " + tablename + ": " + err.Error())
}
}