@@ -6,11 +6,15 @@ import (
6
6
"fmt"
7
7
"io"
8
8
"net/http"
9
+ "net/url"
9
10
"os"
11
+ "os/exec"
10
12
"path/filepath"
13
+ "slices"
11
14
"strings"
12
15
"time"
13
16
17
+ "github.com/cli/safeexec"
14
18
"github.com/k1LoW/errors"
15
19
"github.com/k1LoW/ghfs"
16
20
"github.com/k1LoW/go-github-client/v58/factory"
@@ -28,6 +32,15 @@ import (
28
32
"github.com/xo/dburl"
29
33
)
30
34
35
+ var supportDriversWithDburl = []string {
36
+ "postgres" ,
37
+ "mysql" ,
38
+ "sqlite3" ,
39
+ "sqlserver" ,
40
+ "snowflake" ,
41
+ "clickhouse" ,
42
+ }
43
+
31
44
// Analyze database
32
45
func Analyze (dsn config.DSN ) (_ * schema.Schema , err error ) {
33
46
defer func () {
@@ -57,8 +70,12 @@ func Analyze(dsn config.DSN) (_ *schema.Schema, err error) {
57
70
}
58
71
s := & schema.Schema {}
59
72
u , err := dburl .Parse (urlstr )
73
+ if err != nil || ! slices .Contains (supportDriversWithDburl , u .Driver ) {
74
+ // Try ext driver
75
+ return AnalyzeWithExtDriver (urlstr )
76
+ }
60
77
if err != nil {
61
- return s , err
78
+ return nil , err
62
79
}
63
80
splitted := strings .Split (u .Short (), "/" )
64
81
if len (splitted ) < 2 {
@@ -91,13 +108,13 @@ func Analyze(dsn config.DSN) (_ *schema.Schema, err error) {
91
108
92
109
db , err := dburl .Open (urlstr )
93
110
if err != nil {
94
- return s , errors .WithStack (err )
111
+ return nil , errors .WithStack (err )
95
112
}
96
113
defer func () {
97
114
_ = db .Close ()
98
115
}()
99
116
if err := db .Ping (); err != nil {
100
- return s , errors .WithStack (err )
117
+ return nil , errors .WithStack (err )
101
118
}
102
119
103
120
var driver drivers.Driver
@@ -118,7 +135,7 @@ func Analyze(dsn config.DSN) (_ *schema.Schema, err error) {
118
135
driver , err = mysql .New (db , opts ... )
119
136
}
120
137
if err != nil {
121
- return s , err
138
+ return nil , err
122
139
}
123
140
case "sqlite3" :
124
141
s .Name = splitted [len (splitted )- 1 ]
@@ -137,7 +154,7 @@ func Analyze(dsn config.DSN) (_ *schema.Schema, err error) {
137
154
}
138
155
err = driver .Analyze (s )
139
156
if err != nil {
140
- return s , err
157
+ return nil , err
141
158
}
142
159
return s , nil
143
160
}
@@ -150,23 +167,23 @@ func AnalyzeHTTPResource(dsn config.DSN) (_ *schema.Schema, err error) {
150
167
s := & schema.Schema {}
151
168
req , err := http .NewRequest ("GET" , dsn .URL , nil )
152
169
if err != nil {
153
- return s , err
170
+ return nil , err
154
171
}
155
172
for k , v := range dsn .Headers {
156
173
req .Header .Add (k , v )
157
174
}
158
175
client := & http.Client {Timeout : time .Duration (10 ) * time .Second }
159
176
resp , err := client .Do (req )
160
177
if err != nil {
161
- return s , err
178
+ return nil , err
162
179
}
163
180
defer resp .Body .Close ()
164
181
dec := json .NewDecoder (resp .Body )
165
182
if err := dec .Decode (s ); err != nil {
166
- return s , err
183
+ return nil , err
167
184
}
168
185
if err := s .Repair (); err != nil {
169
- return s , err
186
+ return nil , err
170
187
}
171
188
return s , nil
172
189
}
@@ -197,10 +214,10 @@ func AnalyzeGitHubContent(dsn config.DSN) (_ *schema.Schema, err error) {
197
214
}
198
215
dec := json .NewDecoder (bytes .NewReader (b ))
199
216
if err := dec .Decode (s ); err != nil {
200
- return s , err
217
+ return nil , err
201
218
}
202
219
if err := s .Repair (); err != nil {
203
- return s , err
220
+ return nil , err
204
221
}
205
222
return s , nil
206
223
}
@@ -214,14 +231,14 @@ func AnalyzeJSON(urlstr string) (_ *schema.Schema, err error) {
214
231
splitted := strings .Split (urlstr , "json://" )
215
232
file , err := os .Open (splitted [1 ])
216
233
if err != nil {
217
- return s , err
234
+ return nil , err
218
235
}
219
236
dec := json .NewDecoder (file )
220
237
if err := dec .Decode (s ); err != nil {
221
- return s , err
238
+ return nil , err
222
239
}
223
240
if err := s .Repair (); err != nil {
224
- return s , err
241
+ return nil , err
225
242
}
226
243
return s , nil
227
244
}
@@ -243,15 +260,47 @@ func AnalyzeJSONStringOrFile(strOrPath string) (s *schema.Schema, err error) {
243
260
} else {
244
261
buf , err = os .Open (filepath .Clean (strOrPath ))
245
262
if err != nil {
246
- return s , err
263
+ return nil , err
247
264
}
248
265
}
249
266
dec := json .NewDecoder (buf )
250
267
if err := dec .Decode (s ); err != nil {
251
- return s , err
268
+ return nil , err
269
+ }
270
+ if err := s .Repair (); err != nil {
271
+ return nil , err
272
+ }
273
+ return s , nil
274
+ }
275
+
276
+ // AnalyzeWithExtDriver analyze with external driver command.
277
+ func AnalyzeWithExtDriver (urlstr string ) (* schema.Schema , error ) {
278
+ u , err := url .Parse (urlstr )
279
+ if err != nil {
280
+ return nil , err
281
+ }
282
+ scheme := u .Scheme
283
+ bin , err := safeexec .LookPath (fmt .Sprintf ("tbls-driver-%s" , scheme ))
284
+ if err != nil {
285
+ return nil , fmt .Errorf ("unsupported driver '%s'" , scheme )
286
+ }
287
+ envs := os .Environ ()
288
+ envs = append (envs , fmt .Sprintf ("TBLS_DSN=%s" , urlstr ))
289
+ c := exec .Command (bin )
290
+ buf := new (bytes.Buffer )
291
+ c .Stdout = buf
292
+ c .Stderr = os .Stderr
293
+ c .Env = envs
294
+ if err := c .Run (); err != nil {
295
+ return nil , err
296
+ }
297
+ s := & schema.Schema {}
298
+ dec := json .NewDecoder (buf )
299
+ if err := dec .Decode (s ); err != nil {
300
+ return nil , err
252
301
}
253
302
if err := s .Repair (); err != nil {
254
- return s , err
303
+ return nil , err
255
304
}
256
305
return s , nil
257
306
}
0 commit comments