1" Vim OMNI completion script for SQL
2" Language:    SQL
3" Maintainer:  David Fishburn <dfishburn dot vim at gmail dot com>
4" Version:     7.0
5" Last Change: 2009 Jan 04
6" Usage:       For detailed help
7"              ":help sql.txt"
8"              or ":help ft-sql-omni"
9"              or read $VIMRUNTIME/doc/sql.txt
10
11" History
12" Version 7.0
13"     Better handling of object names
14" Version 6.0
15"     Supports object names with spaces "my table name"
16"
17" Set completion with CTRL-X CTRL-O to autoloaded function.
18" This check is in place in case this script is
19" sourced directly instead of using the autoload feature.
20if exists('&omnifunc')
21    " Do not set the option if already set since this
22    " results in an E117 warning.
23    if &omnifunc == ""
24        setlocal omnifunc=sqlcomplete#Complete
25    endif
26endif
27
28if exists('g:loaded_sql_completion')
29    finish
30endif
31let g:loaded_sql_completion = 70
32
33" Maintains filename of dictionary
34let s:sql_file_table        = ""
35let s:sql_file_procedure    = ""
36let s:sql_file_view         = ""
37
38" Define various arrays to be used for caching
39let s:tbl_name              = []
40let s:tbl_alias             = []
41let s:tbl_cols              = []
42let s:syn_list              = []
43let s:syn_value             = []
44
45" Used in conjunction with the syntaxcomplete plugin
46let s:save_inc              = ""
47let s:save_exc              = ""
48if exists('g:omni_syntax_group_include_sql')
49    let s:save_inc = g:omni_syntax_group_include_sql
50endif
51if exists('g:omni_syntax_group_exclude_sql')
52    let s:save_exc = g:omni_syntax_group_exclude_sql
53endif
54
55" Used with the column list
56let s:save_prev_table       = ""
57
58" Default the option to verify table alias
59if !exists('g:omni_sql_use_tbl_alias')
60    let g:omni_sql_use_tbl_alias = 'a'
61endif
62" Default syntax items to precache
63if !exists('g:omni_sql_precache_syntax_groups')
64    let g:omni_sql_precache_syntax_groups = [
65                \ 'syntax',
66                \ 'sqlKeyword',
67                \ 'sqlFunction',
68                \ 'sqlOption',
69                \ 'sqlType',
70                \ 'sqlStatement'
71                \ ]
72endif
73" Set ignorecase to the ftplugin standard
74if !exists('g:omni_sql_ignorecase')
75    let g:omni_sql_ignorecase = &ignorecase
76endif
77" During table completion, should the table list also
78" include the owner name
79if !exists('g:omni_sql_include_owner')
80    let g:omni_sql_include_owner = 0
81    if exists('g:loaded_dbext')
82        if g:loaded_dbext >= 300
83            " New to dbext 3.00, by default the table lists include the owner
84            " name of the table.  This is used when determining how much of
85            " whatever has been typed should be replaced as part of the
86            " code replacement.
87            let g:omni_sql_include_owner = 1
88        endif
89    endif
90endif
91
92" This function is used for the 'omnifunc' option.
93function! sqlcomplete#Complete(findstart, base)
94
95    " Default to table name completion
96    let compl_type = 'table'
97    " Allow maps to specify what type of object completion they want
98    if exists('b:sql_compl_type')
99        let compl_type = b:sql_compl_type
100    endif
101
102    " First pass through this function determines how much of the line should
103    " be replaced by whatever is chosen from the completion list
104    if a:findstart
105        " Locate the start of the item, including "."
106        let line     = getline('.')
107        let start    = col('.') - 1
108        let lastword = -1
109        let begindot = 0
110        " Check if the first character is a ".", for column completion
111        if line[start - 1] == '.'
112            let begindot = 1
113        endif
114        while start > 0
115            " Additional code was required to handle objects which
116            " can contain spaces like "my table name".
117            if line[start - 1] !~ '\(\w\|\.\)'
118                " If the previous character is not a period or word character
119                break
120            " elseif line[start - 1] =~ '\(\w\|\s\+\)'
121            "     let start -= 1
122            elseif line[start - 1] =~ '\w'
123                " If the previous character is word character continue back
124                let start -= 1
125            elseif line[start - 1] =~ '\.' &&
126                        \ compl_type =~ 'column\|table\|view\|procedure'
127                " If the previous character is a period and we are completing
128                " an object which can be specified with a period like this:
129                "     table_name.column_name
130                "     owner_name.table_name
131
132                " If lastword has already been set for column completion
133                " break from the loop, since we do not also want to pickup
134                " a table name if it was also supplied.
135                if lastword != -1 && compl_type == 'column'
136                    break
137                endif
138                " If column completion was specified stop at the "." if
139                " a . was specified, otherwise, replace all the way up
140                " to the owner name (if included).
141                if lastword == -1 && compl_type == 'column' && begindot == 1
142                    let lastword = start
143                endif
144                " If omni_sql_include_owner = 0, do not include the table
145                " name as part of the substitution, so break here
146                if lastword == -1 &&
147                            \ compl_type =~ 'table\|view\|procedure\column_csv' &&
148                            \ g:omni_sql_include_owner == 0
149                    let lastword = start
150                    break
151                endif
152                let start -= 1
153            else
154                break
155            endif
156        endwhile
157
158        " Return the column of the last word, which is going to be changed.
159        " Remember the text that comes before it in s:prepended.
160        if lastword == -1
161            let s:prepended = ''
162            return start
163        endif
164        let s:prepended = strpart(line, start, lastword - start)
165        return lastword
166    endif
167
168    " Second pass through this function will determine what data to put inside
169    " of the completion list
170    " s:prepended is set by the first pass
171    let base = s:prepended . a:base
172
173    " Default the completion list to an empty list
174    let compl_list = []
175
176    " Default to table name completion
177    let compl_type = 'table'
178    " Allow maps to specify what type of object completion they want
179    if exists('b:sql_compl_type')
180        let compl_type = b:sql_compl_type
181        unlet b:sql_compl_type
182    endif
183
184    if compl_type == 'tableReset'
185        let compl_type = 'table'
186        let base = ''
187    endif
188
189    if compl_type == 'table' ||
190                \ compl_type == 'procedure' ||
191                \ compl_type == 'view'
192
193        " This type of completion relies upon the dbext.vim plugin
194        if s:SQLCCheck4dbext() == -1
195            return []
196        endif
197
198        " Allow the user to override the dbext plugin to specify whether
199        " the owner/creator should be included in the list
200        if g:loaded_dbext >= 300
201            let saveSetting = DB_listOption('dict_show_owner')
202            exec 'DBSetOption dict_show_owner='.(g:omni_sql_include_owner==1?'1':'0')
203        endif
204
205        let compl_type_uc = substitute(compl_type, '\w\+', '\u&', '')
206        " Same call below, no need to do it twice
207        " if s:sql_file_{compl_type} == ""
208        "     let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type_uc)
209        " endif
210        let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type_uc)
211        if s:sql_file_{compl_type} != ""
212            if filereadable(s:sql_file_{compl_type})
213                let compl_list = readfile(s:sql_file_{compl_type})
214            endif
215        endif
216
217        if g:loaded_dbext > 300
218            exec 'DBSetOption dict_show_owner='.saveSetting
219        endif
220    elseif compl_type =~? 'column'
221
222        " This type of completion relies upon the dbext.vim plugin
223        if s:SQLCCheck4dbext() == -1
224            return []
225        endif
226
227        if base == ""
228            " The last time we displayed a column list we stored
229            " the table name.  If the user selects a column list
230            " without a table name of alias present, assume they want
231            " the previous column list displayed.
232            let base = s:save_prev_table
233        endif
234
235        let owner  = ''
236        let column = ''
237
238        if base =~ '\.'
239            " Check if the owner/creator has been specified
240            let owner  = matchstr( base, '^\zs.*\ze\..*\..*' )
241            let table  = matchstr( base, '^\(.*\.\)\?\zs.*\ze\..*' )
242            let column = matchstr( base, '.*\.\zs.*' )
243
244            " It is pretty well impossible to determine if the user
245            " has entered:
246            "    owner.table
247            "    table.column_prefix
248            " So there are a couple of things we can do to mitigate
249            " this issue.
250            "    1.  Check if the dbext plugin has the option turned
251            "        on to even allow owners
252            "    2.  Based on 1, if the user is showing a table list
253            "        and the DrillIntoTable (using <C-Right>) then
254            "        this will be owner.table.  In this case, we can
255            "        check to see the table.column exists in the
256            "        cached table list.  If it does, then we have
257            "        determined the user has actually chosen
258            "        owner.table, not table.column_prefix.
259            let found = -1
260            if g:omni_sql_include_owner == 1 && owner == ''
261                if filereadable(s:sql_file_table)
262                    let tbl_list = readfile(s:sql_file_table)
263                    let found    = index( tbl_list, ((table != '')?(table.'.'):'').column)
264                endif
265            endif
266            " If the table.column was found in the table list, we can safely assume
267            " the owner was not provided and shift the items appropriately.
268            " OR
269            " If the user has indicated not to use table owners at all and
270            " the base ends in a '.' we know they are not providing a column
271            " name, so we can shift the items appropriately.
272            if found != -1 || (g:omni_sql_include_owner == 0 && base !~ '\.$')
273                let owner  = table
274                let table  = column
275                let column = ''
276            endif
277        else
278            let table  = base
279        endif
280
281        " Get anything after the . and consider this the table name
282        " If an owner has been specified, then we must consider the
283        " base to be a partial column name
284        " let base  = matchstr( base, '^\(.*\.\)\?\zs.*' )
285
286        if table != ""
287            let s:save_prev_table = base
288            let list_type         = ''
289
290            if compl_type == 'column_csv'
291                " Return one array element, with a comma separated
292                " list of values instead of multiple array entries
293                " for each column in the table.
294                let list_type     = 'csv'
295            endif
296
297            let compl_list  = s:SQLCGetColumns(table, list_type)
298            if column != ''
299                " If no column prefix has been provided and the table
300                " name was provided, append it to each of the items
301                " returned.
302                let compl_list = map(compl_list, "table.'.'.v:val")
303                if owner != ''
304                    " If an owner has been provided append it to each of the
305                    " items returned.
306                    let compl_list = map(compl_list, "owner.'.'.v:val")
307                endif
308            else
309                let base = ''
310            endif
311
312            if compl_type == 'column_csv'
313                " Join the column array into 1 single element array
314                " but make the columns column separated
315                let compl_list        = [join(compl_list, ', ')]
316            endif
317        endif
318    elseif compl_type == 'resetCache'
319        " Reset all cached items
320        let s:tbl_name  = []
321        let s:tbl_alias = []
322        let s:tbl_cols  = []
323        let s:syn_list  = []
324        let s:syn_value = []
325
326        let msg = "All SQL cached items have been removed."
327        call s:SQLCWarningMsg(msg)
328        " Leave time for the user to read the error message
329        :sleep 2
330    else
331        let compl_list = s:SQLCGetSyntaxList(compl_type)
332    endif
333
334    if base != ''
335        " Filter the list based on the first few characters the user entered.
336        " Check if the text matches at the beginning
337        " or
338        " Match to a owner.table or alias.column type match
339        " or
340        " Handle names with spaces "my table name"
341        let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|^\\(\\w\\+\\.\\)\\?'.base.'\\)"'
342        " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\)"'
343        " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|\\(\\.\\)\\?'.base.'\\)"'
344        " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|\\([^.]*\\)\\?'.base.'\\)"'
345        let compl_list = filter(deepcopy(compl_list), expr)
346    endif
347
348    if exists('b:sql_compl_savefunc') && b:sql_compl_savefunc != ""
349        let &omnifunc = b:sql_compl_savefunc
350    endif
351
352    return compl_list
353endfunc
354
355function! sqlcomplete#PreCacheSyntax(...)
356    let syn_group_arr = []
357    if a:0 > 0
358        let syn_group_arr = a:1
359    else
360        let syn_group_arr = g:omni_sql_precache_syntax_groups
361    endif
362    " For each group specified in the list, precache all
363    " the sytnax items.
364    if !empty(syn_group_arr)
365        for group_name in syn_group_arr
366            call s:SQLCGetSyntaxList(group_name)
367        endfor
368    endif
369endfunction
370
371function! sqlcomplete#Map(type)
372    " Tell the SQL plugin what you want to complete
373    let b:sql_compl_type=a:type
374    " Record previous omnifunc, if the SQL completion
375    " is being used in conjunction with other filetype
376    " completion plugins
377    if &omnifunc != "" && &omnifunc != 'sqlcomplete#Complete'
378        " Record the previous omnifunc, the plugin
379        " will automatically set this back so that it
380        " does not interfere with other ftplugins settings
381        let b:sql_compl_savefunc=&omnifunc
382    endif
383    " Set the OMNI func for the SQL completion plugin
384    let &omnifunc='sqlcomplete#Complete'
385endfunction
386
387function! sqlcomplete#DrillIntoTable()
388    " If the omni popup window is visible
389    if pumvisible()
390        call sqlcomplete#Map('column')
391        " C-Y, makes the currently highlighted entry active
392        " and trigger the omni popup to be redisplayed
393        call feedkeys("\<C-Y>\<C-X>\<C-O>")
394    else
395        if has('win32')
396            " If the popup is not visible, simple perform the normal
397            " <C-Right> behaviour
398            exec "normal! \<C-Right>"
399        endif
400    endif
401    return ""
402endfunction
403
404function! sqlcomplete#DrillOutOfColumns()
405    " If the omni popup window is visible
406    if pumvisible()
407        call sqlcomplete#Map('tableReset')
408        " Trigger the omni popup to be redisplayed
409        call feedkeys("\<C-X>\<C-O>")
410    else
411        if has('win32')
412            " If the popup is not visible, simple perform the normal
413            " <C-Left> behaviour
414            exec "normal! \<C-Left>"
415        endif
416    endif
417    return ""
418endfunction
419
420function! s:SQLCWarningMsg(msg)
421    echohl WarningMsg
422    echomsg a:msg
423    echohl None
424endfunction
425
426function! s:SQLCErrorMsg(msg)
427    echohl ErrorMsg
428    echomsg a:msg
429    echohl None
430endfunction
431
432function! s:SQLCGetSyntaxList(syn_group)
433    let syn_group  = a:syn_group
434    let compl_list = []
435
436    " Check if we have already cached the syntax list
437    let list_idx = index(s:syn_list, syn_group, 0, &ignorecase)
438    if list_idx > -1
439        " Return previously cached value
440        let compl_list = s:syn_value[list_idx]
441    else
442        " Request the syntax list items from the
443        " syntax completion plugin
444        if syn_group == 'syntax'
445            " Handle this special case.  This allows the user
446            " to indicate they want all the syntax items available,
447            " so do not specify a specific include list.
448            let g:omni_syntax_group_include_sql = ''
449        else
450            " The user has specified a specific syntax group
451            let g:omni_syntax_group_include_sql = syn_group
452        endif
453        let g:omni_syntax_group_exclude_sql = ''
454        let syn_value                       = OmniSyntaxList()
455        let g:omni_syntax_group_include_sql = s:save_inc
456        let g:omni_syntax_group_exclude_sql = s:save_exc
457        " Cache these values for later use
458        let s:syn_list  = add( s:syn_list,  syn_group )
459        let s:syn_value = add( s:syn_value, syn_value )
460        let compl_list  = syn_value
461    endif
462
463    return compl_list
464endfunction
465
466function! s:SQLCCheck4dbext()
467    if !exists('g:loaded_dbext')
468        let msg = "The dbext plugin must be loaded for dynamic SQL completion"
469        call s:SQLCErrorMsg(msg)
470        " Leave time for the user to read the error message
471        :sleep 2
472        return -1
473    elseif g:loaded_dbext < 600
474        let msg = "The dbext plugin must be at least version 5.30 " .
475                    \ " for dynamic SQL completion"
476        call s:SQLCErrorMsg(msg)
477        " Leave time for the user to read the error message
478        :sleep 2
479        return -1
480    endif
481    return 1
482endfunction
483
484function! s:SQLCAddAlias(table_name, table_alias, cols)
485    " Strip off the owner if included
486    let table_name  = matchstr(a:table_name, '\%(.\{-}\.\)\?\zs\(.*\)' )
487    let table_alias = a:table_alias
488    let cols        = a:cols
489
490    if g:omni_sql_use_tbl_alias != 'n'
491        if table_alias == ''
492            if 'da' =~? g:omni_sql_use_tbl_alias
493                if table_name =~ '_'
494                    " Treat _ as separators since people often use these
495                    " for word separators
496                    let save_keyword = &iskeyword
497                    setlocal iskeyword-=_
498
499                    " Get the first letter of each word
500                    " [[:alpha:]] is used instead of \w
501                    " to catch extended accented characters
502                    "
503                    let table_alias = substitute(
504                                \ table_name,
505                                \ '\<[[:alpha:]]\+\>_\?',
506                                \ '\=strpart(submatch(0), 0, 1)',
507                                \ 'g'
508                                \ )
509                    " Restore original value
510                    let &iskeyword = save_keyword
511                elseif table_name =~ '\u\U'
512                    let table_alias = substitute(
513                                \ table_name, '\(\u\)\U*', '\1', 'g')
514                else
515                    let table_alias = strpart(table_name, 0, 1)
516                endif
517            endif
518        endif
519        if table_alias != ''
520            " Following a word character, make sure there is a . and no spaces
521            let table_alias = substitute(table_alias, '\w\zs\.\?\s*$', '.', '')
522            if 'a' =~? g:omni_sql_use_tbl_alias && a:table_alias == ''
523                let table_alias = inputdialog("Enter table alias:", table_alias)
524            endif
525        endif
526        if table_alias != ''
527            let cols = substitute(cols, '\<\w', table_alias.'&', 'g')
528        endif
529    endif
530
531    return cols
532endfunction
533
534function! s:SQLCGetObjectOwner(object)
535    " The owner regex matches a word at the start of the string which is
536    " followed by a dot, but doesn't include the dot in the result.
537    " ^           - from beginning of line
538    " \("\|\[\)\? - ignore any quotes
539    " \zs         - start the match now
540    " .\{-}       - get owner name
541    " \ze         - end the match
542    " \("\|\[\)\? - ignore any quotes
543    " \.          - must by followed by a .
544    " let owner = matchstr( a:object, '^\s*\zs.*\ze\.' )
545    let owner = matchstr( a:object, '^\("\|\[\)\?\zs\.\{-}\ze\("\|\]\)\?\.' )
546    return owner
547endfunction
548
549function! s:SQLCGetColumns(table_name, list_type)
550    " Check if the table name was provided as part of the column name
551    let table_name   = matchstr(a:table_name, '^["\[\]a-zA-Z0-9_ ]\+\ze\.\?')
552    let table_cols   = []
553    let table_alias  = ''
554    let move_to_top  = 1
555
556    let table_name   = substitute(table_name, '\s*\(.\{-}\)\s*$', '\1', 'g')
557
558    " If the table name was given as:
559    "     where c.
560    let table_name   = substitute(table_name, '^\c\(WHERE\|AND\|OR\)\s\+', '', '')
561    if g:loaded_dbext >= 300
562        let saveSettingAlias = DB_listOption('use_tbl_alias')
563        exec 'DBSetOption use_tbl_alias=n'
564    endif
565
566    let table_name_stripped = substitute(table_name, '["\[\]]*', '', 'g')
567
568    " Check if we have already cached the column list for this table
569    " by its name
570    let list_idx = index(s:tbl_name, table_name_stripped, 0, &ignorecase)
571    if list_idx > -1
572        let table_cols = split(s:tbl_cols[list_idx], '\n')
573    else
574        " Check if we have already cached the column list for this table
575        " by its alias, assuming the table_name provided was actually
576        " the alias for the table instead
577        "     select *
578        "       from area a
579        "      where a.
580        let list_idx = index(s:tbl_alias, table_name_stripped, 0, &ignorecase)
581        if list_idx > -1
582            let table_alias = table_name_stripped
583            let table_name  = s:tbl_name[list_idx]
584            let table_cols  = split(s:tbl_cols[list_idx], '\n')
585        endif
586    endif
587
588    " If we have not found a cached copy of the table
589    " And the table ends in a "." or we are looking for a column list
590    " if list_idx == -1 && (a:table_name =~ '\.' || b:sql_compl_type =~ 'column')
591    " if list_idx == -1 && (a:table_name =~ '\.' || a:list_type =~ 'csv')
592    if list_idx == -1
593         let saveY      = @y
594         let saveSearch = @/
595         let saveWScan  = &wrapscan
596         let curline    = line(".")
597         let curcol     = col(".")
598
599         " Do not let searchs wrap
600         setlocal nowrapscan
601         " If . was entered, look at the word just before the .
602         " We are looking for something like this:
603         "    select *
604         "      from customer c
605         "     where c.
606         " So when . is pressed, we need to find 'c'
607         "
608
609         " Search backwards to the beginning of the statement
610         " and do NOT wrap
611         " exec 'silent! normal! v?\<\(select\|update\|delete\|;\)\>'."\n".'"yy'
612         exec 'silent! normal! ?\<\(select\|update\|delete\|;\)\>'."\n"
613
614         " Start characterwise visual mode
615         " Advance right one character
616         " Search foward until one of the following:
617         "     1.  Another select/update/delete statement
618         "     2.  A ; at the end of a line (the delimiter)
619         "     3.  The end of the file (incase no delimiter)
620         " Yank the visually selected text into the "y register.
621         exec 'silent! normal! vl/\(\<select\>\|\<update\>\|\<delete\>\|;\s*$\|\%$\)'."\n".'"yy'
622
623         let query = @y
624         let query = substitute(query, "\n", ' ', 'g')
625         let found = 0
626
627         " if query =~? '^\(select\|update\|delete\)'
628         if query =~? '^\(select\)'
629             let found = 1
630             "  \(\(\<\w\+\>\)\.\)\?   -
631             " 'from.\{-}'  - Starting at the from clause
632             " '\zs\(\(\<\w\+\>\)\.\)\?' - Get the owner name (optional)
633             " '\<\w\+\>\ze' - Get the table name
634             " '\s\+\<'.table_name.'\>' - Followed by the alias
635             " '\s*\.\@!.*'  - Cannot be followed by a .
636             " '\(\<where\>\|$\)' - Must be followed by a WHERE clause
637             " '.*'  - Exclude the rest of the line in the match
638             let table_name_new = matchstr(@y,
639                         \ 'from.\{-}'.
640                         \ '\zs\(\("\|\[\)\?.\{-}\("\|\]\)\.\)\?'.
641                         \ '\("\|\[\)\?.\{-}\("\|\]\)\ze'.
642                         \ '\s\+\%(as\s\+\)\?\<'.
643                         \ matchstr(table_name, '.\{-}\ze\.\?$').
644                         \ '\>'.
645                         \ '\s*\.\@!.*'.
646                         \ '\(\<where\>\|$\)'.
647                         \ '.*'
648                         \ )
649
650             if table_name_new != ''
651                 let table_alias = table_name
652                 let table_name  = table_name_new
653
654                 let list_idx = index(s:tbl_name, table_name, 0, &ignorecase)
655                 if list_idx > -1
656                     let table_cols  = split(s:tbl_cols[list_idx])
657                     let s:tbl_name[list_idx]  = table_name
658                     let s:tbl_alias[list_idx] = table_alias
659                 else
660                     let list_idx = index(s:tbl_alias, table_name, 0, &ignorecase)
661                     if list_idx > -1
662                         let table_cols = split(s:tbl_cols[list_idx])
663                         let s:tbl_name[list_idx]  = table_name
664                         let s:tbl_alias[list_idx] = table_alias
665                     endif
666                 endif
667
668             endif
669         else
670             " Simply assume it is a table name provided with a . on the end
671             let found = 1
672         endif
673
674         let @y        = saveY
675         let @/        = saveSearch
676         let &wrapscan = saveWScan
677
678         " Return to previous location
679         call cursor(curline, curcol)
680
681         if found == 0
682             if g:loaded_dbext > 300
683                 exec 'DBSetOption use_tbl_alias='.saveSettingAlias
684             endif
685
686             " Not a SQL statement, do not display a list
687             return []
688         endif
689    endif
690
691    if empty(table_cols)
692        " Specify silent mode, no messages to the user (tbl, 1)
693        " Specify do not comma separate (tbl, 1, 1)
694        let table_cols_str = DB_getListColumn(table_name, 1, 1)
695
696        if table_cols_str != ""
697            let s:tbl_name  = add( s:tbl_name,  table_name )
698            let s:tbl_alias = add( s:tbl_alias, table_alias )
699            let s:tbl_cols  = add( s:tbl_cols,  table_cols_str )
700            let table_cols  = split(table_cols_str, '\n')
701        endif
702
703    endif
704
705    if g:loaded_dbext > 300
706        exec 'DBSetOption use_tbl_alias='.saveSettingAlias
707    endif
708
709    " If the user has asked for a comma separate list of column
710    " values, ask the user if they want to prepend each column
711    " with a tablename alias.
712    if a:list_type == 'csv' && !empty(table_cols)
713        let cols       = join(table_cols, ', ')
714        let cols       = s:SQLCAddAlias(table_name, table_alias, cols)
715        let table_cols = [cols]
716    endif
717
718    return table_cols
719endfunction
720
721