@@ -4,6 +4,7 @@ package drivers
4
4
5
5
import (
6
6
"sort"
7
+ "sync"
7
8
8
9
"github.com/friendsofgo/errors"
9
10
"github.com/volatiletech/sqlboiler/v4/importers"
@@ -17,13 +18,17 @@ const (
17
18
ConfigSchema = "schema"
18
19
ConfigAddEnumTypes = "add-enum-types"
19
20
ConfigEnumNullPrefix = "enum-null-prefix"
21
+ ConfigConcurrency = "concurrency"
20
22
21
23
ConfigUser = "user"
22
24
ConfigPass = "pass"
23
25
ConfigHost = "host"
24
26
ConfigPort = "port"
25
27
ConfigDBName = "dbname"
26
28
ConfigSSLMode = "sslmode"
29
+
30
+ // DefaultConcurrency defines the default amount of threads to use when loading tables info
31
+ DefaultConcurrency = 10
27
32
)
28
33
29
34
// Interface abstracts either a side-effect imported driver or a binary
@@ -102,77 +107,122 @@ type TableColumnTypeTranslator interface {
102
107
// Tables returns the metadata for all tables, minus the tables
103
108
// specified in the blacklist.
104
109
func Tables (c Constructor , schema string , whitelist , blacklist []string ) ([]Table , error ) {
110
+ return TablesConcurrently (c , schema , whitelist , blacklist , 1 )
111
+ }
112
+
113
+ // TablesConcurrently is a concurrent version of Tables. It returns the
114
+ // metadata for all tables, minus the tables specified in the blacklist.
115
+ func TablesConcurrently (c Constructor , schema string , whitelist , blacklist []string , concurrency int ) ([]Table , error ) {
105
116
var err error
117
+ var ret []Table
106
118
107
- names , err := c . TableNames ( schema , whitelist , blacklist )
119
+ ret , err = tables ( c , schema , whitelist , blacklist , concurrency )
108
120
if err != nil {
109
- return nil , errors .Wrap (err , "unable to get table names " )
121
+ return nil , errors .Wrap (err , "unable to load tables " )
110
122
}
111
123
112
- sort .Strings (names )
113
-
114
- var tables []Table
115
- for _ , name := range names {
116
- t := Table {
117
- Name : name ,
124
+ if vc , ok := c .(ViewConstructor ); ok {
125
+ v , err := views (vc , schema , whitelist , blacklist , concurrency )
126
+ if err != nil {
127
+ return nil , errors .Wrap (err , "unable to load views" )
118
128
}
129
+ ret = append (ret , v ... )
130
+ }
119
131
120
- if t .Columns , err = c .Columns (schema , name , whitelist , blacklist ); err != nil {
121
- return nil , errors .Wrapf (err , "unable to fetch table column info (%s)" , name )
122
- }
132
+ return ret , nil
133
+ }
123
134
124
- tr , ok := c .(TableColumnTypeTranslator )
125
- if ok {
126
- for i , col := range t .Columns {
127
- t .Columns [i ] = tr .TranslateTableColumnType (col , name )
128
- }
129
- } else {
130
- for i , col := range t .Columns {
131
- t .Columns [i ] = c .TranslateColumnType (col )
132
- }
133
- }
135
+ func tables (c Constructor , schema string , whitelist , blacklist []string , concurrency int ) ([]Table , error ) {
136
+ var err error
134
137
135
- if t .PKey , err = c .PrimaryKeyInfo (schema , name ); err != nil {
136
- return nil , errors .Wrapf (err , "unable to fetch table pkey info (%s)" , name )
137
- }
138
+ names , err := c .TableNames (schema , whitelist , blacklist )
139
+ if err != nil {
140
+ return nil , errors .Wrap (err , "unable to get table names" )
141
+ }
138
142
139
- if t .FKeys , err = c .ForeignKeyInfo (schema , name ); err != nil {
140
- return nil , errors .Wrapf (err , "unable to fetch table fkey info (%s)" , name )
141
- }
143
+ sort .Strings (names )
142
144
143
- filterPrimaryKey (& t , whitelist , blacklist )
144
- filterForeignKeys (& t , whitelist , blacklist )
145
+ ret := make ([]Table , len (names ))
146
+
147
+ limiter := newConcurrencyLimiter (concurrency )
148
+ wg := sync.WaitGroup {}
149
+ errs := make (chan error , len (names ))
150
+ for i , name := range names {
151
+ wg .Add (1 )
152
+ limiter .get ()
153
+ go func (i int , name string ) {
154
+ defer wg .Done ()
155
+ defer limiter .put ()
156
+ t , err := table (c , schema , name , whitelist , blacklist )
157
+ if err != nil {
158
+ errs <- err
159
+ return
160
+ }
161
+ ret [i ] = t
162
+ }(i , name )
163
+ }
145
164
146
- setIsJoinTable ( & t )
165
+ wg . Wait ( )
147
166
148
- tables = append (tables , t )
167
+ // return first error occurred if any
168
+ if len (errs ) > 0 {
169
+ return nil , <- errs
149
170
}
150
171
151
172
// Relationships have a dependency on foreign key nullability.
152
- for i := range tables {
153
- tbl := & tables [i ]
154
- setForeignKeyConstraints (tbl , tables )
173
+ for i := range ret {
174
+ tbl := & ret [i ]
175
+ setForeignKeyConstraints (tbl , ret )
155
176
}
156
- for i := range tables {
157
- tbl := & tables [i ]
158
- setRelationships (tbl , tables )
177
+ for i := range ret {
178
+ tbl := & ret [i ]
179
+ setRelationships (tbl , ret )
159
180
}
160
181
161
- if vc , ok := c .(ViewConstructor ); ok {
162
- viewTables , err := views (vc , schema , whitelist , blacklist )
163
- if err != nil {
164
- return nil , errors .Wrap (err , "unable to load views" )
182
+ return ret , nil
183
+ }
184
+
185
+ // table returns columns info for a given table
186
+ func table (c Constructor , schema string , name string , whitelist , blacklist []string ) (Table , error ) {
187
+ var err error
188
+ t := & Table {
189
+ Name : name ,
190
+ }
191
+
192
+ if t .Columns , err = c .Columns (schema , name , whitelist , blacklist ); err != nil {
193
+ return Table {}, errors .Wrapf (err , "unable to fetch table column info (%s)" , name )
194
+ }
195
+
196
+ tr , ok := c .(TableColumnTypeTranslator )
197
+ if ok {
198
+ for i , col := range t .Columns {
199
+ t .Columns [i ] = tr .TranslateTableColumnType (col , name )
165
200
}
201
+ } else {
202
+ for i , col := range t .Columns {
203
+ t .Columns [i ] = c .TranslateColumnType (col )
204
+ }
205
+ }
166
206
167
- tables = append (tables , viewTables ... )
207
+ if t .PKey , err = c .PrimaryKeyInfo (schema , name ); err != nil {
208
+ return Table {}, errors .Wrapf (err , "unable to fetch table pkey info (%s)" , name )
168
209
}
169
210
170
- return tables , nil
211
+ if t .FKeys , err = c .ForeignKeyInfo (schema , name ); err != nil {
212
+ return Table {}, errors .Wrapf (err , "unable to fetch table fkey info (%s)" , name )
213
+ }
214
+
215
+ filterPrimaryKey (t , whitelist , blacklist )
216
+ filterForeignKeys (t , whitelist , blacklist )
217
+
218
+ setIsJoinTable (t )
219
+
220
+ return * t , nil
171
221
}
172
222
173
223
// views returns the metadata for all views, minus the views
174
224
// specified in the blacklist.
175
- func views (c ViewConstructor , schema string , whitelist , blacklist []string ) ([]Table , error ) {
225
+ func views (c ViewConstructor , schema string , whitelist , blacklist []string , concurrency int ) ([]Table , error ) {
176
226
var err error
177
227
178
228
names , err := c .ViewNames (schema , whitelist , blacklist )
@@ -182,44 +232,71 @@ func views(c ViewConstructor, schema string, whitelist, blacklist []string) ([]T
182
232
183
233
sort .Strings (names )
184
234
185
- var views []Table
186
- for _ , name := range names {
187
- t := Table {
188
- IsView : true ,
189
- Name : name ,
190
- }
235
+ ret := make ([]Table , len (names ))
236
+
237
+ limiter := newConcurrencyLimiter (concurrency )
238
+ wg := sync.WaitGroup {}
239
+ errs := make (chan error , len (names ))
240
+ for i , name := range names {
241
+ wg .Add (1 )
242
+ limiter .get ()
243
+ go func (i int , name string ) {
244
+ defer wg .Done ()
245
+ defer limiter .put ()
246
+ t , err := view (c , schema , name , whitelist , blacklist )
247
+ if err != nil {
248
+ errs <- err
249
+ return
250
+ }
251
+ ret [i ] = t
252
+ }(i , name )
253
+ }
191
254
192
- if t .ViewCapabilities , err = c .ViewCapabilities (schema , name ); err != nil {
193
- return nil , errors .Wrapf (err , "unable to fetch view capabilities info (%s)" , name )
194
- }
255
+ wg .Wait ()
195
256
196
- if t .Columns , err = c .ViewColumns (schema , name , whitelist , blacklist ); err != nil {
197
- return nil , errors .Wrapf (err , "unable to fetch view column info (%s)" , name )
198
- }
257
+ // return first error occurred if any
258
+ if len (errs ) > 0 {
259
+ return nil , <- errs
260
+ }
199
261
200
- tr , ok := c .(TableColumnTypeTranslator )
201
- if ok {
202
- for i , col := range t .Columns {
203
- t .Columns [i ] = tr .TranslateTableColumnType (col , name )
204
- }
205
- } else {
206
- for i , col := range t .Columns {
207
- t .Columns [i ] = c .TranslateColumnType (col )
208
- }
209
- }
262
+ return ret , nil
263
+ }
210
264
211
- views = append (views , t )
265
+ // view returns columns info for a given view
266
+ func view (c ViewConstructor , schema string , name string , whitelist , blacklist []string ) (Table , error ) {
267
+ var err error
268
+ t := Table {
269
+ IsView : true ,
270
+ Name : name ,
212
271
}
213
272
214
- return views , nil
273
+ if t .ViewCapabilities , err = c .ViewCapabilities (schema , name ); err != nil {
274
+ return Table {}, errors .Wrapf (err , "unable to fetch view capabilities info (%s)" , name )
275
+ }
276
+
277
+ if t .Columns , err = c .ViewColumns (schema , name , whitelist , blacklist ); err != nil {
278
+ return Table {}, errors .Wrapf (err , "unable to fetch view column info (%s)" , name )
279
+ }
280
+
281
+ tr , ok := c .(TableColumnTypeTranslator )
282
+ if ok {
283
+ for i , col := range t .Columns {
284
+ t .Columns [i ] = tr .TranslateTableColumnType (col , name )
285
+ }
286
+ } else {
287
+ for i , col := range t .Columns {
288
+ t .Columns [i ] = c .TranslateColumnType (col )
289
+ }
290
+ }
291
+
292
+ return t , nil
215
293
}
216
294
217
295
func knownColumn (table string , column string , whitelist , blacklist []string ) bool {
218
296
return (len (whitelist ) == 0 ||
219
297
strmangle .SetInclude (table , whitelist ) ||
220
298
strmangle .SetInclude (table + "." + column , whitelist ) ||
221
299
strmangle .SetInclude ("*." + column , whitelist )) &&
222
-
223
300
(len (blacklist ) == 0 || (! strmangle .SetInclude (table , blacklist ) &&
224
301
! strmangle .SetInclude (table + "." + column , blacklist ) &&
225
302
! strmangle .SetInclude ("*." + column , blacklist )))
@@ -294,3 +371,23 @@ func setRelationships(t *Table, tables []Table) {
294
371
t .ToOneRelationships = toOneRelationships (* t , tables )
295
372
t .ToManyRelationships = toManyRelationships (* t , tables )
296
373
}
374
+
375
+ // concurrencyCounter is a helper structure that can limit amount of concurrently processed requests
376
+ type concurrencyLimiter chan struct {}
377
+
378
+ func newConcurrencyLimiter (capacity int ) concurrencyLimiter {
379
+ ret := make (concurrencyLimiter , capacity )
380
+ for i := 0 ; i < capacity ; i ++ {
381
+ ret <- struct {}{}
382
+ }
383
+
384
+ return ret
385
+ }
386
+
387
+ func (c concurrencyLimiter ) get () {
388
+ <- c
389
+ }
390
+
391
+ func (c concurrencyLimiter ) put () {
392
+ c <- struct {}{}
393
+ }
0 commit comments