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