@@ -5,15 +5,137 @@ package duckdb
5
5
6
6
import (
7
7
"context"
8
+ "database/sql"
9
+ "fmt"
8
10
"io"
11
+ "strings"
9
12
10
13
_ "github.com/marcboeker/go-duckdb" // DRIVER
11
14
"github.com/xo/usql/drivers"
12
15
"github.com/xo/usql/drivers/metadata"
16
+ infos "github.com/xo/usql/drivers/metadata/informationschema"
13
17
mymeta "github.com/xo/usql/drivers/metadata/mysql"
14
18
)
15
19
20
+ type metaReader struct {
21
+ metadata.LoggingReader
22
+ }
23
+
24
+ var (
25
+ _ metadata.CatalogReader = & metaReader {}
26
+ _ metadata.ColumnStatReader = & metaReader {}
27
+ )
28
+
29
+ func (r metaReader ) Catalogs (metadata.Filter ) (* metadata.CatalogSet , error ) {
30
+ qstr := `SHOW catalogs`
31
+ rows , closeRows , err := r .Query (qstr )
32
+ if err != nil {
33
+ return nil , err
34
+ }
35
+ defer closeRows ()
36
+
37
+ results := []metadata.Catalog {}
38
+ for rows .Next () {
39
+ rec := metadata.Catalog {}
40
+ err = rows .Scan (& rec .Catalog )
41
+ if err != nil {
42
+ return nil , err
43
+ }
44
+ results = append (results , rec )
45
+ }
46
+ if rows .Err () != nil {
47
+ return nil , rows .Err ()
48
+ }
49
+ return metadata .NewCatalogSet (results ), nil
50
+ }
51
+
52
+ func (r metaReader ) ColumnStats (f metadata.Filter ) (* metadata.ColumnStatSet , error ) {
53
+ names := []string {}
54
+ if f .Catalog != "" {
55
+ names = append (names , f .Catalog + "." )
56
+ }
57
+ if f .Schema != "" {
58
+ names = append (names , f .Schema + "." )
59
+ }
60
+ names = append (names , f .Parent )
61
+ rows , closeRows , err := r .Query (fmt .Sprintf ("SHOW STATS FOR %s" , strings .Join (names , "" )))
62
+ if err != nil {
63
+ return nil , err
64
+ }
65
+ defer closeRows ()
66
+
67
+ results := []metadata.ColumnStat {}
68
+ for rows .Next () {
69
+ rec := metadata.ColumnStat {Catalog : f .Catalog , Schema : f .Schema , Table : f .Parent }
70
+ name := sql.NullString {}
71
+ avgWidth := sql.NullInt32 {}
72
+ numDistinct := sql.NullInt64 {}
73
+ nullFrac := sql.NullFloat64 {}
74
+ numRows := sql.NullInt64 {}
75
+ min := sql.NullString {}
76
+ max := sql.NullString {}
77
+ err = rows .Scan (
78
+ & name ,
79
+ & avgWidth ,
80
+ & numDistinct ,
81
+ & nullFrac ,
82
+ & numRows ,
83
+ & min ,
84
+ & max ,
85
+ )
86
+ if err != nil {
87
+ return nil , err
88
+ }
89
+ if ! name .Valid {
90
+ continue
91
+ }
92
+ rec .Name = name .String
93
+ if avgWidth .Valid {
94
+ rec .AvgWidth = int (avgWidth .Int32 )
95
+ }
96
+ if numDistinct .Valid {
97
+ rec .NumDistinct = numDistinct .Int64
98
+ }
99
+ if nullFrac .Valid {
100
+ rec .NullFrac = nullFrac .Float64
101
+ }
102
+ if min .Valid {
103
+ rec .Min = min .String
104
+ }
105
+ if max .Valid {
106
+ rec .Max = max .String
107
+ }
108
+ results = append (results , rec )
109
+ }
110
+ if rows .Err () != nil {
111
+ return nil , rows .Err ()
112
+ }
113
+
114
+ return metadata .NewColumnStatSet (results ), nil
115
+ }
116
+
16
117
func init () {
118
+ newReader := func (db drivers.DB , opts ... metadata.ReaderOption ) metadata.Reader {
119
+ ir := infos .New (
120
+ infos .WithPlaceholder (func (int ) string { return "?" }),
121
+ infos .WithCustomClauses (map [infos.ClauseName ]string {
122
+ infos .ColumnsColumnSize : "0" ,
123
+ infos .ColumnsNumericScale : "0" ,
124
+ infos .ColumnsNumericPrecRadix : "0" ,
125
+ infos .ColumnsCharOctetLength : "0" ,
126
+ }),
127
+ infos .WithFunctions (false ),
128
+ infos .WithSequences (false ),
129
+ infos .WithIndexes (false ),
130
+ infos .WithConstraints (false ),
131
+ infos .WithColumnPrivileges (false ),
132
+ infos .WithUsagePrivileges (false ),
133
+ )(db , opts ... )
134
+ mr := & metaReader {
135
+ LoggingReader : metadata .NewLoggingReader (db , opts ... ),
136
+ }
137
+ return metadata .NewPluginReader (ir , mr )
138
+ }
17
139
drivers .Register ("duckdb" , drivers.Driver {
18
140
AllowMultilineComments : true ,
19
141
Version : func (ctx context.Context , db drivers.DB ) (string , error ) {
@@ -24,9 +146,9 @@ func init() {
24
146
}
25
147
return "DuckDB " + ver , nil
26
148
},
27
- NewMetadataReader : mymeta . NewReader ,
149
+ NewMetadataReader : newReader ,
28
150
NewMetadataWriter : func (db drivers.DB , w io.Writer , opts ... metadata.ReaderOption ) metadata.Writer {
29
- return metadata .NewDefaultWriter (mymeta . NewReader (db , opts ... ))(db , w )
151
+ return metadata .NewDefaultWriter (newReader (db , opts ... ))(db , w )
30
152
},
31
153
Copy : drivers .CopyWithInsert (func (int ) string { return "?" }),
32
154
NewCompleter : mymeta .NewCompleter ,
0 commit comments