Skip to content

Commit 600e561

Browse files
committed
Add support for grouped database connections
1 parent f4d37b7 commit 600e561

File tree

7 files changed

+398
-51
lines changed

7 files changed

+398
-51
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ Just make sure to **NOT COMMIT** these. I suggest using project local vim config
151151
Using `:DBUIAddConnection` command or pressing `A` in dbui drawer opens up a prompt to enter database url and name,
152152
that will be saved in `g:db_ui_save_location` connections file. These connections are available from everywhere.
153153

154+
To add a connection to a group, prefix the name with a group path, starting with a forward slash:
155+
* `/Production/users` adds a connection named "users" to the "Production" group
156+
* `/Production/EU/orders` adds a connection named "orders" to the "EU" subgroup under "Production"
157+
158+
Groups can be nested to create hierarchical organization. Non-existent groups will be created automatically.
159+
154160
#### Connection related notes
155161
It is possible to have two connections with same name, but from different source.
156162
for example, you can have `my-db` in env variable, in `g:dbs` and in saved connections.

autoload/db_ui.vim

Lines changed: 116 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ endfunction
163163
function! s:dbui.new() abort
164164
let instance = copy(self)
165165
let instance.dbs = {}
166+
let instance.groups = {}
166167
let instance.dbs_list = []
167168
let instance.save_path = ''
168169
let instance.connections_path = ''
@@ -215,16 +216,52 @@ function! s:dbui.populate_dbs() abort
215216
call self.populate_from_connections_file()
216217

217218
for db in self.dbs_list
218-
let key_name = printf('%s_%s', db.name, db.source)
219-
if !has_key(self.dbs, key_name) || db.url !=? self.dbs[key_name].url
220-
let new_entry = self.generate_new_db_entry(db)
221-
if !empty(new_entry)
222-
let self.dbs[key_name] = new_entry
223-
endif
224-
else
225-
let self.dbs[key_name] = self.drawer.populate(self.dbs[key_name])
219+
call self.populate_item(db)
220+
endfor
221+
222+
endfunction
223+
224+
function! s:dbui.populate_item(db) abort
225+
if self.is_group(a:db)
226+
return self.populate_group(a:db)
226227
endif
228+
229+
return self.populate_db(a:db)
230+
endfunction
231+
232+
function! s:dbui.populate_group(db) abort
233+
for conn in a:db.connections
234+
call self.populate_item(conn)
227235
endfor
236+
237+
if !has_key(self.groups, a:db.key_name)
238+
let self.groups[a:db.key_name] = self.generate_new_group_entry(a:db)
239+
endif
240+
endfunction
241+
242+
function! s:dbui.populate_db(db) abort
243+
let key_name = a:db.key_name
244+
245+
if !has_key(self.dbs, key_name) || a:db.url !=? self.dbs[key_name].url
246+
let new_entry = self.generate_new_db_entry(a:db)
247+
248+
if !empty(new_entry)
249+
let self.dbs[key_name] = new_entry
250+
endif
251+
252+
else
253+
let self.dbs[key_name] = self.drawer.populate(self.dbs[key_name])
254+
endif
255+
endfunction
256+
257+
function! s:dbui.generate_new_group_entry(db) abort
258+
return {
259+
\ 'name': a:db.name,
260+
\ 'source': a:db.source,
261+
\ 'expanded': 0,
262+
\ 'key_name': a:db.key_name,
263+
\ 'path': a:db.path,
264+
\ }
228265
endfunction
229266

230267
function! s:dbui.generate_new_db_entry(db) abort
@@ -235,7 +272,7 @@ function! s:dbui.generate_new_db_entry(db) abort
235272
let db_name = substitute(get(parsed_url, 'path', ''), '^\/', '', '')
236273
let save_path = ''
237274
if !empty(self.save_path)
238-
let save_path = printf('%s/%s', self.save_path, a:db.name)
275+
let save_path = printf('%s/%s/%s', self.save_path, a:db.path, a:db.name)
239276
endif
240277
let buffers = filter(copy(self.old_buffers), 'fnamemodify(v:val, ":e") =~? "^".a:db.name."-" || fnamemodify(v:val, ":t") =~? "^".a:db.name."-"')
241278

@@ -255,11 +292,12 @@ function! s:dbui.generate_new_db_entry(db) abort
255292
\ 'save_path': save_path,
256293
\ 'db_name': !empty(db_name) ? db_name : a:db.name,
257294
\ 'name': a:db.name,
258-
\ 'key_name': printf('%s_%s', a:db.name, a:db.source),
295+
\ 'key_name': a:db.key_name,
259296
\ 'schema_support': 0,
260297
\ 'quote': 0,
261298
\ 'default_scheme': '',
262-
\ 'filetype': ''
299+
\ 'filetype': '',
300+
\ 'path': a:db.path,
263301
\ }
264302

265303
call self.populate_schema_info(db)
@@ -288,7 +326,7 @@ function! s:dbui.populate_from_global_variable() abort
288326
if exists('g:db') && !empty(g:db)
289327
let url = self.resolve_url_global_variable(g:db)
290328
let gdb_name = split(url, '/')[-1]
291-
call self.add_if_not_exists(gdb_name, url, 'g:dbs')
329+
call self.add_if_not_exists(gdb_name, url, '', 'g:dbs')
292330
endif
293331

294332
if !exists('g:dbs') || empty(g:dbs)
@@ -297,13 +335,13 @@ function! s:dbui.populate_from_global_variable() abort
297335

298336
if type(g:dbs) ==? type({})
299337
for [db_name, Db_url] in items(g:dbs)
300-
call self.add_if_not_exists(db_name, self.resolve_url_global_variable(Db_url), 'g:dbs')
338+
call self.add_if_not_exists(db_name, self.resolve_url_global_variable(Db_url), '', 'g:dbs')
301339
endfor
302340
return self
303341
endif
304342

305343
for db in g:dbs
306-
call self.add_if_not_exists(db.name, self.resolve_url_global_variable(db.url), 'g:dbs')
344+
call self.add_if_not_exists(db.name, self.resolve_url_global_variable(db.url), '', 'g:dbs')
307345
endfor
308346

309347
return self
@@ -326,7 +364,7 @@ function! s:dbui.populate_from_dotenv() abort
326364
for [name, url] in items(all_envs)
327365
if stridx(name, prefix) != -1
328366
let db_name = tolower(join(split(name, prefix)))
329-
call self.add_if_not_exists(db_name, url, 'dotenv')
367+
call self.add_if_not_exists(db_name, url, '', 'dotenv')
330368
endif
331369
endfor
332370
endfunction
@@ -350,7 +388,7 @@ function! s:dbui.populate_from_env() abort
350388
\ printf('Found %s variable for db url, but unable to parse the name. Please provide name via %s', g:db_ui_env_variable_url, g:db_ui_env_variable_name))
351389
endif
352390

353-
call self.add_if_not_exists(env_name, env_url, 'env')
391+
call self.add_if_not_exists(env_name, env_url, '', 'env')
354392
return self
355393
endfunction
356394

@@ -371,22 +409,71 @@ function! s:dbui.populate_from_connections_file() abort
371409
let file = db_ui#utils#readfile(self.connections_path)
372410

373411
for conn in file
374-
call self.add_if_not_exists(conn.name, conn.url, 'file')
412+
call self.process_connection(conn, [])
375413
endfor
376414

377415
return self
378416
endfunction
379417

380-
function! s:dbui.add_if_not_exists(name, url, source) abort
381-
let existing = get(filter(copy(self.dbs_list), 'v:val.name ==? a:name && v:val.source ==? a:source'), 0, {})
418+
function! s:dbui.process_connection(conn, parent_groups) abort
419+
if self.is_group(a:conn)
420+
let new_groups = copy(a:parent_groups)
421+
call add(new_groups, a:conn.name)
422+
423+
for c in a:conn.connections
424+
call self.process_connection(c, new_groups)
425+
endfor
426+
return
427+
endif
428+
429+
let group_path = join(a:parent_groups, '/')
430+
call self.add_if_not_exists(a:conn.name, a:conn.url, group_path, 'file')
431+
endfunction
432+
433+
function! s:dbui.add_if_not_exists(name, url, group_path, source) abort
434+
let dbs_list = self.get_target_list(a:group_path, a:source)
435+
let existing = get(filter(copy(dbs_list), 'v:val.name ==? a:name && v:val.source ==? a:source'), 0, {})
436+
382437
if !empty(existing)
383438
return db_ui#notifications#warning(printf('Warning: Duplicate connection name "%s" in "%s" source. First one added has precedence.', a:name, a:source))
384439
endif
385-
return add(self.dbs_list, {
386-
\ 'name': a:name, 'url': db_ui#resolve(a:url), 'source': a:source, 'key_name': printf('%s_%s', a:name, a:source)
440+
441+
return add(dbs_list, {
442+
\ 'name': a:name,
443+
\ 'url': db_ui#resolve(a:url),
444+
\ 'source': a:source,
445+
\ 'key_name': empty(a:group_path) ? printf('%s_%s', a:name, a:source) : printf('%s/%s_%s', a:group_path, a:name, a:source),
446+
\ 'path': a:group_path,
387447
\ })
388448
endfunction
389449

450+
function! s:dbui.get_target_list(group_path, source) abort
451+
let dbs_list = self.dbs_list
452+
453+
if !empty(a:group_path)
454+
let groups = split(a:group_path, '/')
455+
let current_path = ''
456+
457+
for group in groups
458+
let existing_group = get(filter(copy(dbs_list), 'v:val.name ==? group && v:val.source ==? a:source'), 0, {})
459+
if empty(existing_group)
460+
let existing_group = {
461+
\ 'name': group,
462+
\ 'source': a:source,
463+
\ 'key_name': printf('%s/%s_%s', current_path, group, a:source),
464+
\ 'connections': [],
465+
\ 'path': current_path,
466+
\ }
467+
call add(dbs_list, existing_group)
468+
endif
469+
let dbs_list = existing_group.connections
470+
let current_path = printf('%s/%s', current_path, group)
471+
endfor
472+
endif
473+
474+
return dbs_list
475+
endfunction
476+
390477
function! s:dbui.is_tmp_location_buffer(db, buf) abort
391478
if index(a:db.buffers.tmp, a:buf) > -1
392479
return 1
@@ -485,3 +572,11 @@ function! s:get_db(saved_query_db) abort
485572
let selected_db = s:dbui_instance.dbs[selected_db.key_name]
486573
return selected_db
487574
endfunction
575+
576+
function! s:dbui.is_group(item) abort
577+
return has_key(a:item, 'connections')
578+
endfunction
579+
580+
function! s:dbui.is_connection(item) abort
581+
return has_key(a:item, 'url')
582+
endfunction

0 commit comments

Comments
 (0)