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