1*f193fffdSBram Moolenaar" Vim OMNI completion script for SQL
2e2f98b95SBram Moolenaar" Language:    SQL
3e2f98b95SBram Moolenaar" Maintainer:  David Fishburn <[email protected]>
4*f193fffdSBram Moolenaar" Version:     4.0
5*f193fffdSBram Moolenaar" Last Change: Wed Apr 26 2006 3:00:06 PM
6*f193fffdSBram Moolenaar" Usage:       For detailed help
7*f193fffdSBram Moolenaar"              ":help sql.txt"
8*f193fffdSBram Moolenaar"              or ":help ft-sql-omni"
9*f193fffdSBram Moolenaar"              or read $VIMRUNTIME/doc/sql.txt
10e2f98b95SBram Moolenaar
11e2f98b95SBram Moolenaar" Set completion with CTRL-X CTRL-O to autoloaded function.
12e2f98b95SBram Moolenaar" This check is in place in case this script is
13e2f98b95SBram Moolenaar" sourced directly instead of using the autoload feature.
14e2f98b95SBram Moolenaarif exists('&omnifunc')
15e2f98b95SBram Moolenaar    " Do not set the option if already set since this
16e2f98b95SBram Moolenaar    " results in an E117 warning.
17e2f98b95SBram Moolenaar    if &omnifunc == ""
18e2f98b95SBram Moolenaar        setlocal omnifunc=sqlcomplete#Complete
19e2f98b95SBram Moolenaar    endif
20e2f98b95SBram Moolenaarendif
21e2f98b95SBram Moolenaar
22e2f98b95SBram Moolenaarif exists('g:loaded_sql_completion')
23e2f98b95SBram Moolenaar    finish
24e2f98b95SBram Moolenaarendif
25*f193fffdSBram Moolenaarlet g:loaded_sql_completion = 40
26e2f98b95SBram Moolenaar
27e2f98b95SBram Moolenaar" Maintains filename of dictionary
28e2f98b95SBram Moolenaarlet s:sql_file_table        = ""
29e2f98b95SBram Moolenaarlet s:sql_file_procedure    = ""
30e2f98b95SBram Moolenaarlet s:sql_file_view         = ""
31e2f98b95SBram Moolenaar
32e2f98b95SBram Moolenaar" Define various arrays to be used for caching
33e2f98b95SBram Moolenaarlet s:tbl_name              = []
34e2f98b95SBram Moolenaarlet s:tbl_alias             = []
35e2f98b95SBram Moolenaarlet s:tbl_cols              = []
36e2f98b95SBram Moolenaarlet s:syn_list              = []
37e2f98b95SBram Moolenaarlet s:syn_value             = []
38e2f98b95SBram Moolenaar
39e2f98b95SBram Moolenaar" Used in conjunction with the syntaxcomplete plugin
40e2f98b95SBram Moolenaarlet s:save_inc              = ""
41e2f98b95SBram Moolenaarlet s:save_exc              = ""
42e2f98b95SBram Moolenaarif exists('g:omni_syntax_group_include_sql')
43e2f98b95SBram Moolenaar    let s:save_inc = g:omni_syntax_group_include_sql
44e2f98b95SBram Moolenaarendif
45e2f98b95SBram Moolenaarif exists('g:omni_syntax_group_exclude_sql')
46e2f98b95SBram Moolenaar    let s:save_exc = g:omni_syntax_group_exclude_sql
47e2f98b95SBram Moolenaarendif
48e2f98b95SBram Moolenaar
49e2f98b95SBram Moolenaar" Used with the column list
50e2f98b95SBram Moolenaarlet s:save_prev_table       = ""
51e2f98b95SBram Moolenaar
52e2f98b95SBram Moolenaar" Default the option to verify table alias
53e2f98b95SBram Moolenaarif !exists('g:omni_sql_use_tbl_alias')
54e2f98b95SBram Moolenaar    let g:omni_sql_use_tbl_alias = 'a'
55e2f98b95SBram Moolenaarendif
56910f66f9SBram Moolenaar" Default syntax items to precache
57910f66f9SBram Moolenaarif !exists('g:omni_sql_precache_syntax_groups')
58910f66f9SBram Moolenaar    let g:omni_sql_precache_syntax_groups = [
59910f66f9SBram Moolenaar                \ 'syntax',
60910f66f9SBram Moolenaar                \ 'sqlKeyword',
61910f66f9SBram Moolenaar                \ 'sqlFunction',
62910f66f9SBram Moolenaar                \ 'sqlOption',
63910f66f9SBram Moolenaar                \ 'sqlType',
64910f66f9SBram Moolenaar                \ 'sqlStatement'
65910f66f9SBram Moolenaar                \ ]
66910f66f9SBram Moolenaarendif
67eb3593b3SBram Moolenaar" Set ignorecase to the ftplugin standard
68eb3593b3SBram Moolenaarif !exists('g:omni_sql_ignorecase')
69eb3593b3SBram Moolenaar    let g:omni_sql_ignorecase = &ignorecase
70eb3593b3SBram Moolenaarendif
71eb3593b3SBram Moolenaar" During table completion, should the table list also
72eb3593b3SBram Moolenaar" include the owner name
73eb3593b3SBram Moolenaarif !exists('g:omni_sql_include_owner')
74eb3593b3SBram Moolenaar    let g:omni_sql_include_owner = 0
75eb3593b3SBram Moolenaar    if exists('g:loaded_dbext')
76eb3593b3SBram Moolenaar        if g:loaded_dbext >= 300
77eb3593b3SBram Moolenaar            " New to dbext 3.00, by default the table lists include the owner
78eb3593b3SBram Moolenaar            " name of the table.  This is used when determining how much of
79eb3593b3SBram Moolenaar            " whatever has been typed should be replaced as part of the
80eb3593b3SBram Moolenaar            " code replacement.
81eb3593b3SBram Moolenaar            let g:omni_sql_include_owner = 1
82eb3593b3SBram Moolenaar        endif
83eb3593b3SBram Moolenaar    endif
84eb3593b3SBram Moolenaarendif
85e2f98b95SBram Moolenaar
86e2f98b95SBram Moolenaar" This function is used for the 'omnifunc' option.
87e2f98b95SBram Moolenaarfunction! sqlcomplete#Complete(findstart, base)
88e2f98b95SBram Moolenaar
89e2f98b95SBram Moolenaar    " Default to table name completion
90e2f98b95SBram Moolenaar    let compl_type = 'table'
91e2f98b95SBram Moolenaar    " Allow maps to specify what type of object completion they want
92e2f98b95SBram Moolenaar    if exists('b:sql_compl_type')
93e2f98b95SBram Moolenaar        let compl_type = b:sql_compl_type
94e2f98b95SBram Moolenaar    endif
95e2f98b95SBram Moolenaar
96910f66f9SBram Moolenaar    " First pass through this function determines how much of the line should
97910f66f9SBram Moolenaar    " be replaced by whatever is chosen from the completion list
98e2f98b95SBram Moolenaar    if a:findstart
99e2f98b95SBram Moolenaar        " Locate the start of the item, including "."
100e2f98b95SBram Moolenaar        let line     = getline('.')
101e2f98b95SBram Moolenaar        let start    = col('.') - 1
102e2f98b95SBram Moolenaar        let lastword = -1
103*f193fffdSBram Moolenaar        let begindot = 0
104*f193fffdSBram Moolenaar        " Check if the first character is a ".", for column completion
105*f193fffdSBram Moolenaar        if line[start - 1] == '.'
106*f193fffdSBram Moolenaar            let begindot = 1
107*f193fffdSBram Moolenaar        endif
108e2f98b95SBram Moolenaar        while start > 0
109e2f98b95SBram Moolenaar            if line[start - 1] =~ '\w'
110e2f98b95SBram Moolenaar                let start -= 1
111eb3593b3SBram Moolenaar            elseif line[start - 1] =~ '\.' &&
112eb3593b3SBram Moolenaar                        \ compl_type =~ 'column\|table\|view\|procedure'
113eb3593b3SBram Moolenaar                " If lastword has already been set for column completion
114eb3593b3SBram Moolenaar                " break from the loop, since we do not also want to pickup
115eb3593b3SBram Moolenaar                " a table name if it was also supplied.
116*f193fffdSBram Moolenaar                if lastword != -1 && compl_type == 'column'
117eb3593b3SBram Moolenaar                    break
118eb3593b3SBram Moolenaar                endif
119*f193fffdSBram Moolenaar                " If column completion was specified stop at the "." if
120*f193fffdSBram Moolenaar                " a . was specified, otherwise, replace all the way up
121*f193fffdSBram Moolenaar                " to the owner name (if included).
122*f193fffdSBram Moolenaar                if lastword == -1 && compl_type == 'column' && begindot == 1
123e2f98b95SBram Moolenaar                    let lastword = start
124e2f98b95SBram Moolenaar                endif
125eb3593b3SBram Moolenaar                " If omni_sql_include_owner = 0, do not include the table
126eb3593b3SBram Moolenaar                " name as part of the substitution, so break here
127eb3593b3SBram Moolenaar                if lastword == -1 &&
128*f193fffdSBram Moolenaar                            \ compl_type =~ 'table\|view\|procedure\column_csv' &&
129eb3593b3SBram Moolenaar                            \ g:omni_sql_include_owner == 0
130eb3593b3SBram Moolenaar                    let lastword = start
131eb3593b3SBram Moolenaar                    break
132eb3593b3SBram Moolenaar                endif
133e2f98b95SBram Moolenaar                let start -= 1
134e2f98b95SBram Moolenaar            else
135e2f98b95SBram Moolenaar                break
136e2f98b95SBram Moolenaar            endif
137e2f98b95SBram Moolenaar        endwhile
138e2f98b95SBram Moolenaar
139e2f98b95SBram Moolenaar        " Return the column of the last word, which is going to be changed.
140e2f98b95SBram Moolenaar        " Remember the text that comes before it in s:prepended.
141e2f98b95SBram Moolenaar        if lastword == -1
142e2f98b95SBram Moolenaar            let s:prepended = ''
143e2f98b95SBram Moolenaar            return start
144e2f98b95SBram Moolenaar        endif
145e2f98b95SBram Moolenaar        let s:prepended = strpart(line, start, lastword - start)
146e2f98b95SBram Moolenaar        return lastword
147e2f98b95SBram Moolenaar    endif
148e2f98b95SBram Moolenaar
149910f66f9SBram Moolenaar    " Second pass through this function will determine what data to put inside
150910f66f9SBram Moolenaar    " of the completion list
151910f66f9SBram Moolenaar    " s:prepended is set by the first pass
152e2f98b95SBram Moolenaar    let base = s:prepended . a:base
153e2f98b95SBram Moolenaar
154910f66f9SBram Moolenaar    " Default the completion list to an empty list
155e2f98b95SBram Moolenaar    let compl_list = []
156e2f98b95SBram Moolenaar
157e2f98b95SBram Moolenaar    " Default to table name completion
158e2f98b95SBram Moolenaar    let compl_type = 'table'
159e2f98b95SBram Moolenaar    " Allow maps to specify what type of object completion they want
160e2f98b95SBram Moolenaar    if exists('b:sql_compl_type')
161e2f98b95SBram Moolenaar        let compl_type = b:sql_compl_type
162e2f98b95SBram Moolenaar        unlet b:sql_compl_type
163e2f98b95SBram Moolenaar    endif
164e2f98b95SBram Moolenaar
165e2f98b95SBram Moolenaar    if compl_type == 'tableReset'
166e2f98b95SBram Moolenaar        let compl_type = 'table'
167e2f98b95SBram Moolenaar        let base = ''
168e2f98b95SBram Moolenaar    endif
169e2f98b95SBram Moolenaar
170e2f98b95SBram Moolenaar    if compl_type == 'table' ||
171e2f98b95SBram Moolenaar                \ compl_type == 'procedure' ||
172e2f98b95SBram Moolenaar                \ compl_type == 'view'
173e2f98b95SBram Moolenaar
174e2f98b95SBram Moolenaar        " This type of completion relies upon the dbext.vim plugin
175e2f98b95SBram Moolenaar        if s:SQLCCheck4dbext() == -1
176e2f98b95SBram Moolenaar            return []
177e2f98b95SBram Moolenaar        endif
178e2f98b95SBram Moolenaar
179e2f98b95SBram Moolenaar        if s:sql_file_{compl_type} == ""
180e2f98b95SBram Moolenaar            let compl_type = substitute(compl_type, '\w\+', '\u&', '')
181e2f98b95SBram Moolenaar            let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type)
182e2f98b95SBram Moolenaar        endif
183e2f98b95SBram Moolenaar        let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type)
184e2f98b95SBram Moolenaar        if s:sql_file_{compl_type} != ""
185e2f98b95SBram Moolenaar            if filereadable(s:sql_file_{compl_type})
186e2f98b95SBram Moolenaar                let compl_list = readfile(s:sql_file_{compl_type})
187eb3593b3SBram Moolenaar                " let dic_list = readfile(s:sql_file_{compl_type})
188eb3593b3SBram Moolenaar                " if !empty(dic_list)
189eb3593b3SBram Moolenaar                "     for elem in dic_list
190eb3593b3SBram Moolenaar                "         let kind = (compl_type=='table'?'m':(compl_type=='procedure'?'f':'v'))
191eb3593b3SBram Moolenaar                "         let item = {'word':elem, 'menu':elem, 'kind':kind, 'info':compl_type}
192eb3593b3SBram Moolenaar                "         let compl_list += [item]
193eb3593b3SBram Moolenaar                "     endfor
194eb3593b3SBram Moolenaar                " endif
195e2f98b95SBram Moolenaar            endif
196e2f98b95SBram Moolenaar        endif
197e2f98b95SBram Moolenaar    elseif compl_type == 'column'
198e2f98b95SBram Moolenaar
199e2f98b95SBram Moolenaar        " This type of completion relies upon the dbext.vim plugin
200e2f98b95SBram Moolenaar        if s:SQLCCheck4dbext() == -1
201e2f98b95SBram Moolenaar            return []
202e2f98b95SBram Moolenaar        endif
203e2f98b95SBram Moolenaar
204e2f98b95SBram Moolenaar        if base == ""
205e2f98b95SBram Moolenaar            " The last time we displayed a column list we stored
206e2f98b95SBram Moolenaar            " the table name.  If the user selects a column list
207e2f98b95SBram Moolenaar            " without a table name of alias present, assume they want
208e2f98b95SBram Moolenaar            " the previous column list displayed.
209e2f98b95SBram Moolenaar            let base = s:save_prev_table
210e2f98b95SBram Moolenaar        endif
211e2f98b95SBram Moolenaar
212e2f98b95SBram Moolenaar        if base != ""
213e2f98b95SBram Moolenaar            let compl_list        = s:SQLCGetColumns(base, '')
214e2f98b95SBram Moolenaar            let s:save_prev_table = base
215e2f98b95SBram Moolenaar            let base              = ''
216e2f98b95SBram Moolenaar        endif
217e2f98b95SBram Moolenaar    elseif compl_type == 'column_csv'
218e2f98b95SBram Moolenaar
219e2f98b95SBram Moolenaar        " This type of completion relies upon the dbext.vim plugin
220e2f98b95SBram Moolenaar        if s:SQLCCheck4dbext() == -1
221e2f98b95SBram Moolenaar            return []
222e2f98b95SBram Moolenaar        endif
223e2f98b95SBram Moolenaar
224e2f98b95SBram Moolenaar        if base == ""
225e2f98b95SBram Moolenaar            " The last time we displayed a column list we stored
226e2f98b95SBram Moolenaar            " the table name.  If the user selects a column list
227e2f98b95SBram Moolenaar            " without a table name of alias present, assume they want
228e2f98b95SBram Moolenaar            " the previous column list displayed.
229e2f98b95SBram Moolenaar            let base = s:save_prev_table
230e2f98b95SBram Moolenaar        endif
231e2f98b95SBram Moolenaar
232e2f98b95SBram Moolenaar        if base != ""
233e2f98b95SBram Moolenaar            let compl_list        = s:SQLCGetColumns(base, 'csv')
234e2f98b95SBram Moolenaar            let s:save_prev_table = base
235e2f98b95SBram Moolenaar            " Join the column array into 1 single element array
236e2f98b95SBram Moolenaar            " but make the columns column separated
237e2f98b95SBram Moolenaar            let compl_list        = [join(compl_list, ', ')]
238e2f98b95SBram Moolenaar            let base              = ''
239e2f98b95SBram Moolenaar        endif
240e2f98b95SBram Moolenaar    elseif compl_type == 'resetCache'
241e2f98b95SBram Moolenaar        " Reset all cached items
242e2f98b95SBram Moolenaar        let s:tbl_name  = []
243e2f98b95SBram Moolenaar        let s:tbl_alias = []
244e2f98b95SBram Moolenaar        let s:tbl_cols  = []
245e2f98b95SBram Moolenaar        let s:syn_list  = []
246e2f98b95SBram Moolenaar        let s:syn_value = []
247*f193fffdSBram Moolenaar
248*f193fffdSBram Moolenaar        let msg = "All SQL cached items have been removed."
249*f193fffdSBram Moolenaar        call s:SQLCWarningMsg(msg)
250*f193fffdSBram Moolenaar        " Leave time for the user to read the error message
251*f193fffdSBram Moolenaar        :sleep 2
252e2f98b95SBram Moolenaar    else
253910f66f9SBram Moolenaar        let compl_list = s:SQLCGetSyntaxList(compl_type)
254e2f98b95SBram Moolenaar    endif
255e2f98b95SBram Moolenaar
256e2f98b95SBram Moolenaar    if base != ''
257e2f98b95SBram Moolenaar        " Filter the list based on the first few characters the user
258e2f98b95SBram Moolenaar        " entered
259eb3593b3SBram Moolenaar        let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "^'.base.'"'
260eb3593b3SBram Moolenaar        let compl_list = filter(deepcopy(compl_list), expr)
261e2f98b95SBram Moolenaar    endif
262e2f98b95SBram Moolenaar
263910f66f9SBram Moolenaar    if exists('b:sql_compl_savefunc') && b:sql_compl_savefunc != ""
264910f66f9SBram Moolenaar        let &omnifunc = b:sql_compl_savefunc
265910f66f9SBram Moolenaar    endif
266910f66f9SBram Moolenaar
267e2f98b95SBram Moolenaar    return compl_list
268e2f98b95SBram Moolenaarendfunc
269e2f98b95SBram Moolenaar
270910f66f9SBram Moolenaarfunction! sqlcomplete#PreCacheSyntax(...)
271910f66f9SBram Moolenaar    let syn_group_arr = []
272910f66f9SBram Moolenaar    if a:0 > 0
273910f66f9SBram Moolenaar        let syn_group_arr = a:1
274910f66f9SBram Moolenaar    else
275910f66f9SBram Moolenaar        let syn_group_arr = g:omni_sql_precache_syntax_groups
276910f66f9SBram Moolenaar    endif
277910f66f9SBram Moolenaar    if !empty(syn_group_arr)
278910f66f9SBram Moolenaar        for group_name in syn_group_arr
279910f66f9SBram Moolenaar            call s:SQLCGetSyntaxList(group_name)
280910f66f9SBram Moolenaar        endfor
281910f66f9SBram Moolenaar    endif
282910f66f9SBram Moolenaarendfunction
283910f66f9SBram Moolenaar
284910f66f9SBram Moolenaarfunction! sqlcomplete#Map(type)
285910f66f9SBram Moolenaar    " Tell the SQL plugin what you want to complete
286910f66f9SBram Moolenaar    let b:sql_compl_type=a:type
287910f66f9SBram Moolenaar    " Record previous omnifunc, if the SQL completion
288910f66f9SBram Moolenaar    " is being used in conjunction with other filetype
289910f66f9SBram Moolenaar    " completion plugins
290910f66f9SBram Moolenaar    if &omnifunc != "" && &omnifunc != 'sqlcomplete#Complete'
291910f66f9SBram Moolenaar        " Record the previous omnifunc, the plugin
292910f66f9SBram Moolenaar        " will automatically set this back so that it
293910f66f9SBram Moolenaar        " does not interfere with other ftplugins settings
294910f66f9SBram Moolenaar        let b:sql_compl_savefunc=&omnifunc
295910f66f9SBram Moolenaar    endif
296910f66f9SBram Moolenaar    " Set the OMNI func for the SQL completion plugin
297910f66f9SBram Moolenaar    let &omnifunc='sqlcomplete#Complete'
298910f66f9SBram Moolenaarendfunction
299910f66f9SBram Moolenaar
300*f193fffdSBram Moolenaarfunction! sqlcomplete#DrillIntoTable()
301*f193fffdSBram Moolenaar    " If the omni popup window is visible
302*f193fffdSBram Moolenaar    if pumvisible()
303*f193fffdSBram Moolenaar        call sqlcomplete#Map('column')
304*f193fffdSBram Moolenaar        " C-Y, makes the currently highlighted entry active
305*f193fffdSBram Moolenaar        " and trigger the omni popup to be redisplayed
306*f193fffdSBram Moolenaar        call feedkeys("\<C-Y>\<C-X>\<C-O>")
307*f193fffdSBram Moolenaar    else
308*f193fffdSBram Moolenaar        if has('win32')
309*f193fffdSBram Moolenaar            " If the popup is not visible, simple perform the normal
310*f193fffdSBram Moolenaar            " <C-Right> behaviour
311*f193fffdSBram Moolenaar            exec "normal! \<C-Right>"
312*f193fffdSBram Moolenaar        endif
313*f193fffdSBram Moolenaar    endif
314*f193fffdSBram Moolenaar    return ""
315*f193fffdSBram Moolenaarendfunction
316*f193fffdSBram Moolenaar
317*f193fffdSBram Moolenaarfunction! sqlcomplete#DrillOutOfColumns()
318*f193fffdSBram Moolenaar    " If the omni popup window is visible
319*f193fffdSBram Moolenaar    if pumvisible()
320*f193fffdSBram Moolenaar        call sqlcomplete#Map('tableReset')
321*f193fffdSBram Moolenaar        " Trigger the omni popup to be redisplayed
322*f193fffdSBram Moolenaar        call feedkeys("\<C-X>\<C-O>")
323*f193fffdSBram Moolenaar    else
324*f193fffdSBram Moolenaar        if has('win32')
325*f193fffdSBram Moolenaar            " If the popup is not visible, simple perform the normal
326*f193fffdSBram Moolenaar            " <C-Left> behaviour
327*f193fffdSBram Moolenaar            exec "normal! \<C-Left>"
328*f193fffdSBram Moolenaar        endif
329*f193fffdSBram Moolenaar    endif
330*f193fffdSBram Moolenaar    return ""
331*f193fffdSBram Moolenaarendfunction
332*f193fffdSBram Moolenaar
333*f193fffdSBram Moolenaarfunction! s:SQLCWarningMsg(msg)
334*f193fffdSBram Moolenaar    echohl WarningMsg
335*f193fffdSBram Moolenaar    echomsg a:msg
336*f193fffdSBram Moolenaar    echohl None
337*f193fffdSBram Moolenaarendfunction
338*f193fffdSBram Moolenaar
339*f193fffdSBram Moolenaarfunction! s:SQLCErrorMsg(msg)
340*f193fffdSBram Moolenaar    echohl ErrorMsg
341*f193fffdSBram Moolenaar    echomsg a:msg
342*f193fffdSBram Moolenaar    echohl None
343*f193fffdSBram Moolenaarendfunction
344*f193fffdSBram Moolenaar
345910f66f9SBram Moolenaarfunction! s:SQLCGetSyntaxList(syn_group)
346910f66f9SBram Moolenaar    let syn_group  = a:syn_group
347910f66f9SBram Moolenaar    let compl_list = []
348910f66f9SBram Moolenaar
349910f66f9SBram Moolenaar    " Check if we have already cached the syntax list
350910f66f9SBram Moolenaar    let list_idx = index(s:syn_list, syn_group, 0, &ignorecase)
351910f66f9SBram Moolenaar    if list_idx > -1
352910f66f9SBram Moolenaar        " Return previously cached value
353910f66f9SBram Moolenaar        let compl_list = s:syn_value[list_idx]
354910f66f9SBram Moolenaar    else
355910f66f9SBram Moolenaar        " Request the syntax list items from the
356910f66f9SBram Moolenaar        " syntax completion plugin
357910f66f9SBram Moolenaar        if syn_group == 'syntax'
358910f66f9SBram Moolenaar            " Handle this special case.  This allows the user
359910f66f9SBram Moolenaar            " to indicate they want all the syntax items available,
360910f66f9SBram Moolenaar            " so do not specify a specific include list.
361910f66f9SBram Moolenaar            let g:omni_syntax_group_include_sql = ''
362910f66f9SBram Moolenaar        else
363910f66f9SBram Moolenaar            " The user has specified a specific syntax group
364910f66f9SBram Moolenaar            let g:omni_syntax_group_include_sql = syn_group
365910f66f9SBram Moolenaar        endif
366910f66f9SBram Moolenaar        let g:omni_syntax_group_exclude_sql = ''
367910f66f9SBram Moolenaar        let syn_value                       = OmniSyntaxList()
368910f66f9SBram Moolenaar        let g:omni_syntax_group_include_sql = s:save_inc
369910f66f9SBram Moolenaar        let g:omni_syntax_group_exclude_sql = s:save_exc
370910f66f9SBram Moolenaar        " Cache these values for later use
371910f66f9SBram Moolenaar        let s:syn_list  = add( s:syn_list,  syn_group )
372910f66f9SBram Moolenaar        let s:syn_value = add( s:syn_value, syn_value )
373910f66f9SBram Moolenaar        let compl_list  = syn_value
374910f66f9SBram Moolenaar    endif
375910f66f9SBram Moolenaar
376910f66f9SBram Moolenaar    return compl_list
377910f66f9SBram Moolenaarendfunction
378910f66f9SBram Moolenaar
379e2f98b95SBram Moolenaarfunction! s:SQLCCheck4dbext()
380e2f98b95SBram Moolenaar    if !exists('g:loaded_dbext')
381e2f98b95SBram Moolenaar        let msg = "The dbext plugin must be loaded for dynamic SQL completion"
382e2f98b95SBram Moolenaar        call s:SQLCErrorMsg(msg)
383e2f98b95SBram Moolenaar        " Leave time for the user to read the error message
384e2f98b95SBram Moolenaar        :sleep 2
385e2f98b95SBram Moolenaar        return -1
386eb3593b3SBram Moolenaar    elseif g:loaded_dbext < 300
387eb3593b3SBram Moolenaar        let msg = "The dbext plugin must be at least version 3.00 " .
388e2f98b95SBram Moolenaar                    \ " for dynamic SQL completion"
389e2f98b95SBram Moolenaar        call s:SQLCErrorMsg(msg)
390e2f98b95SBram Moolenaar        " Leave time for the user to read the error message
391e2f98b95SBram Moolenaar        :sleep 2
392e2f98b95SBram Moolenaar        return -1
393e2f98b95SBram Moolenaar    endif
394e2f98b95SBram Moolenaar    return 1
395e2f98b95SBram Moolenaarendfunction
396e2f98b95SBram Moolenaar
397e2f98b95SBram Moolenaarfunction! s:SQLCAddAlias(table_name, table_alias, cols)
398*f193fffdSBram Moolenaar    " Strip off the owner if included
399*f193fffdSBram Moolenaar    let table_name  = matchstr(a:table_name, '\%(.\{-}\.\)\?\zs\(.*\)' )
400e2f98b95SBram Moolenaar    let table_alias = a:table_alias
401e2f98b95SBram Moolenaar    let cols        = a:cols
402e2f98b95SBram Moolenaar
403e2f98b95SBram Moolenaar    if g:omni_sql_use_tbl_alias != 'n'
404e2f98b95SBram Moolenaar        if table_alias == ''
405e2f98b95SBram Moolenaar            if 'da' =~? g:omni_sql_use_tbl_alias
406e2f98b95SBram Moolenaar                if table_name =~ '_'
407e2f98b95SBram Moolenaar                    " Treat _ as separators since people often use these
408e2f98b95SBram Moolenaar                    " for word separators
409e2f98b95SBram Moolenaar                    let save_keyword = &iskeyword
410e2f98b95SBram Moolenaar                    setlocal iskeyword-=_
411e2f98b95SBram Moolenaar
412e2f98b95SBram Moolenaar                    " Get the first letter of each word
413e2f98b95SBram Moolenaar                    " [[:alpha:]] is used instead of \w
414e2f98b95SBram Moolenaar                    " to catch extended accented characters
415e2f98b95SBram Moolenaar                    "
416e2f98b95SBram Moolenaar                    let table_alias = substitute(
417e2f98b95SBram Moolenaar                                \ table_name,
418e2f98b95SBram Moolenaar                                \ '\<[[:alpha:]]\+\>_\?',
419e2f98b95SBram Moolenaar                                \ '\=strpart(submatch(0), 0, 1)',
420e2f98b95SBram Moolenaar                                \ 'g'
421e2f98b95SBram Moolenaar                                \ )
422e2f98b95SBram Moolenaar                    " Restore original value
423e2f98b95SBram Moolenaar                    let &iskeyword = save_keyword
424e2f98b95SBram Moolenaar                elseif table_name =~ '\u\U'
425*f193fffdSBram Moolenaar                    let table_alias = substitute(
426e2f98b95SBram Moolenaar                                \ table_name, '\(\u\)\U*', '\1', 'g')
427e2f98b95SBram Moolenaar                else
428e2f98b95SBram Moolenaar                    let table_alias = strpart(table_name, 0, 1)
429e2f98b95SBram Moolenaar                endif
430e2f98b95SBram Moolenaar            endif
431e2f98b95SBram Moolenaar        endif
432e2f98b95SBram Moolenaar        if table_alias != ''
433e2f98b95SBram Moolenaar            " Following a word character, make sure there is a . and no spaces
434e2f98b95SBram Moolenaar            let table_alias = substitute(table_alias, '\w\zs\.\?\s*$', '.', '')
435e2f98b95SBram Moolenaar            if 'a' =~? g:omni_sql_use_tbl_alias && a:table_alias == ''
436e2f98b95SBram Moolenaar                let table_alias = inputdialog("Enter table alias:", table_alias)
437e2f98b95SBram Moolenaar            endif
438e2f98b95SBram Moolenaar        endif
439e2f98b95SBram Moolenaar        if table_alias != ''
440e2f98b95SBram Moolenaar            let cols = substitute(cols, '\<\w', table_alias.'&', 'g')
441e2f98b95SBram Moolenaar        endif
442e2f98b95SBram Moolenaar    endif
443e2f98b95SBram Moolenaar
444e2f98b95SBram Moolenaar    return cols
445e2f98b95SBram Moolenaarendfunction
446e2f98b95SBram Moolenaar
447e2f98b95SBram Moolenaarfunction! s:SQLCGetColumns(table_name, list_type)
448e2f98b95SBram Moolenaar    let table_name   = matchstr(a:table_name, '^\w\+')
449*f193fffdSBram Moolenaar    let table_name   = matchstr(a:table_name, '^[a-zA-Z0-9_.]\+')
450e2f98b95SBram Moolenaar    let table_cols   = []
451e2f98b95SBram Moolenaar    let table_alias  = ''
452e2f98b95SBram Moolenaar    let move_to_top  = 1
453e2f98b95SBram Moolenaar
454eb3593b3SBram Moolenaar    if g:loaded_dbext >= 300
455e2f98b95SBram Moolenaar        let saveSettingAlias = DB_listOption('use_tbl_alias')
456e2f98b95SBram Moolenaar        exec 'DBSetOption use_tbl_alias=n'
457e2f98b95SBram Moolenaar    endif
458e2f98b95SBram Moolenaar
459e2f98b95SBram Moolenaar    " Check if we have already cached the column list for this table
460e2f98b95SBram Moolenaar    " by its name
461e2f98b95SBram Moolenaar    let list_idx = index(s:tbl_name, table_name, 0, &ignorecase)
462e2f98b95SBram Moolenaar    if list_idx > -1
463e2f98b95SBram Moolenaar        let table_cols = split(s:tbl_cols[list_idx])
464e2f98b95SBram Moolenaar    else
465e2f98b95SBram Moolenaar        " Check if we have already cached the column list for this table
466e2f98b95SBram Moolenaar        " by its alias, assuming the table_name provided was actually
467e2f98b95SBram Moolenaar        " the alias for the table instead
468e2f98b95SBram Moolenaar        "     select *
469e2f98b95SBram Moolenaar        "       from area a
470e2f98b95SBram Moolenaar        "      where a.
471e2f98b95SBram Moolenaar        let list_idx = index(s:tbl_alias, table_name, 0, &ignorecase)
472e2f98b95SBram Moolenaar        if list_idx > -1
473e2f98b95SBram Moolenaar            let table_alias = table_name
474e2f98b95SBram Moolenaar            let table_name  = s:tbl_name[list_idx]
475e2f98b95SBram Moolenaar            let table_cols  = split(s:tbl_cols[list_idx])
476e2f98b95SBram Moolenaar        endif
477e2f98b95SBram Moolenaar    endif
478e2f98b95SBram Moolenaar
479e2f98b95SBram Moolenaar    " If we have not found a cached copy of the table
480e2f98b95SBram Moolenaar    " And the table ends in a "." or we are looking for a column list
481e2f98b95SBram Moolenaar    " if list_idx == -1 && (a:table_name =~ '\.' || b:sql_compl_type =~ 'column')
482e2f98b95SBram Moolenaar    " if list_idx == -1 && (a:table_name =~ '\.' || a:list_type =~ 'csv')
483e2f98b95SBram Moolenaar    if list_idx == -1
484e2f98b95SBram Moolenaar         let saveY      = @y
485e2f98b95SBram Moolenaar         let saveSearch = @/
486e2f98b95SBram Moolenaar         let saveWScan  = &wrapscan
487e2f98b95SBram Moolenaar         let curline    = line(".")
488e2f98b95SBram Moolenaar         let curcol     = col(".")
489e2f98b95SBram Moolenaar
490e2f98b95SBram Moolenaar         " Do not let searchs wrap
491e2f98b95SBram Moolenaar         setlocal nowrapscan
492e2f98b95SBram Moolenaar         " If . was entered, look at the word just before the .
493e2f98b95SBram Moolenaar         " We are looking for something like this:
494e2f98b95SBram Moolenaar         "    select *
495e2f98b95SBram Moolenaar         "      from customer c
496e2f98b95SBram Moolenaar         "     where c.
497e2f98b95SBram Moolenaar         " So when . is pressed, we need to find 'c'
498e2f98b95SBram Moolenaar         "
499e2f98b95SBram Moolenaar
500e2f98b95SBram Moolenaar         " Search backwards to the beginning of the statement
501e2f98b95SBram Moolenaar         " and do NOT wrap
502e2f98b95SBram Moolenaar         " exec 'silent! normal! v?\<\(select\|update\|delete\|;\)\>'."\n".'"yy'
503e2f98b95SBram Moolenaar         exec 'silent! normal! ?\<\(select\|update\|delete\|;\)\>'."\n"
504e2f98b95SBram Moolenaar
505e2f98b95SBram Moolenaar         " Start characterwise visual mode
506e2f98b95SBram Moolenaar         " Advance right one character
507e2f98b95SBram Moolenaar         " Search foward until one of the following:
508e2f98b95SBram Moolenaar         "     1.  Another select/update/delete statement
509e2f98b95SBram Moolenaar         "     2.  A ; at the end of a line (the delimiter)
510e2f98b95SBram Moolenaar         "     3.  The end of the file (incase no delimiter)
511e2f98b95SBram Moolenaar         " Yank the visually selected text into the "y register.
512e2f98b95SBram Moolenaar         exec 'silent! normal! vl/\(\<select\>\|\<update\>\|\<delete\>\|;\s*$\|\%$\)'."\n".'"yy'
513e2f98b95SBram Moolenaar
514e2f98b95SBram Moolenaar         let query = @y
515e2f98b95SBram Moolenaar         let query = substitute(query, "\n", ' ', 'g')
516e2f98b95SBram Moolenaar         let found = 0
517e2f98b95SBram Moolenaar
518e2f98b95SBram Moolenaar         " if query =~? '^\(select\|update\|delete\)'
519e2f98b95SBram Moolenaar         if query =~? '^\(select\)'
520e2f98b95SBram Moolenaar             let found = 1
521e2f98b95SBram Moolenaar             "  \(\(\<\w\+\>\)\.\)\?   -
522e2f98b95SBram Moolenaar             " 'from.\{-}'  - Starting at the from clause
523e2f98b95SBram Moolenaar             " '\zs\(\(\<\w\+\>\)\.\)\?' - Get the owner name (optional)
524e2f98b95SBram Moolenaar             " '\<\w\+\>\ze' - Get the table name
525e2f98b95SBram Moolenaar             " '\s\+\<'.table_name.'\>' - Followed by the alias
526e2f98b95SBram Moolenaar             " '\s*\.\@!.*'  - Cannot be followed by a .
527e2f98b95SBram Moolenaar             " '\(\<where\>\|$\)' - Must be followed by a WHERE clause
528e2f98b95SBram Moolenaar             " '.*'  - Exclude the rest of the line in the match
529e2f98b95SBram Moolenaar             let table_name_new = matchstr(@y,
530e2f98b95SBram Moolenaar                         \ 'from.\{-}'.
531e2f98b95SBram Moolenaar                         \ '\zs\(\(\<\w\+\>\)\.\)\?'.
532e2f98b95SBram Moolenaar                         \ '\<\w\+\>\ze'.
533*f193fffdSBram Moolenaar                         \ '\s\+\%(as\s\+\)\?\<'.
534*f193fffdSBram Moolenaar                         \ matchstr(table_name, '.\{-}\ze\.\?$').
535*f193fffdSBram Moolenaar                         \ '\>'.
536e2f98b95SBram Moolenaar                         \ '\s*\.\@!.*'.
537e2f98b95SBram Moolenaar                         \ '\(\<where\>\|$\)'.
538e2f98b95SBram Moolenaar                         \ '.*'
539e2f98b95SBram Moolenaar                         \ )
540e2f98b95SBram Moolenaar             if table_name_new != ''
541e2f98b95SBram Moolenaar                 let table_alias = table_name
542e2f98b95SBram Moolenaar                 let table_name  = table_name_new
543e2f98b95SBram Moolenaar
544e2f98b95SBram Moolenaar                 let list_idx = index(s:tbl_name, table_name, 0, &ignorecase)
545e2f98b95SBram Moolenaar                 if list_idx > -1
546e2f98b95SBram Moolenaar                     let table_cols  = split(s:tbl_cols[list_idx])
547e2f98b95SBram Moolenaar                     let s:tbl_name[list_idx]  = table_name
548e2f98b95SBram Moolenaar                     let s:tbl_alias[list_idx] = table_alias
549e2f98b95SBram Moolenaar                 else
550e2f98b95SBram Moolenaar                     let list_idx = index(s:tbl_alias, table_name, 0, &ignorecase)
551e2f98b95SBram Moolenaar                     if list_idx > -1
552e2f98b95SBram Moolenaar                         let table_cols = split(s:tbl_cols[list_idx])
553e2f98b95SBram Moolenaar                         let s:tbl_name[list_idx]  = table_name
554e2f98b95SBram Moolenaar                         let s:tbl_alias[list_idx] = table_alias
555e2f98b95SBram Moolenaar                     endif
556e2f98b95SBram Moolenaar                 endif
557e2f98b95SBram Moolenaar
558e2f98b95SBram Moolenaar             endif
559e2f98b95SBram Moolenaar         else
560e2f98b95SBram Moolenaar             " Simply assume it is a table name provided with a . on the end
561e2f98b95SBram Moolenaar             let found = 1
562e2f98b95SBram Moolenaar         endif
563e2f98b95SBram Moolenaar
564e2f98b95SBram Moolenaar         let @y        = saveY
565e2f98b95SBram Moolenaar         let @/        = saveSearch
566e2f98b95SBram Moolenaar         let &wrapscan = saveWScan
567e2f98b95SBram Moolenaar
568e2f98b95SBram Moolenaar         " Return to previous location
569e2f98b95SBram Moolenaar         call cursor(curline, curcol)
570e2f98b95SBram Moolenaar
571e2f98b95SBram Moolenaar         if found == 0
572eb3593b3SBram Moolenaar             if g:loaded_dbext > 300
573e2f98b95SBram Moolenaar                 exec 'DBSetOption use_tbl_alias='.saveSettingAlias
574e2f98b95SBram Moolenaar             endif
575e2f98b95SBram Moolenaar
576e2f98b95SBram Moolenaar             " Not a SQL statement, do not display a list
577e2f98b95SBram Moolenaar             return []
578e2f98b95SBram Moolenaar         endif
579e2f98b95SBram Moolenaar    endif
580e2f98b95SBram Moolenaar
581e2f98b95SBram Moolenaar    if empty(table_cols)
582e2f98b95SBram Moolenaar        " Specify silent mode, no messages to the user (tbl, 1)
583e2f98b95SBram Moolenaar        " Specify do not comma separate (tbl, 1, 1)
584e2f98b95SBram Moolenaar        let table_cols_str = DB_getListColumn(table_name, 1, 1)
585e2f98b95SBram Moolenaar
586e2f98b95SBram Moolenaar        if table_cols_str != ""
587e2f98b95SBram Moolenaar            let s:tbl_name  = add( s:tbl_name,  table_name )
588e2f98b95SBram Moolenaar            let s:tbl_alias = add( s:tbl_alias, table_alias )
589e2f98b95SBram Moolenaar            let s:tbl_cols  = add( s:tbl_cols,  table_cols_str )
590e2f98b95SBram Moolenaar            let table_cols  = split(table_cols_str)
591e2f98b95SBram Moolenaar        endif
592e2f98b95SBram Moolenaar
593e2f98b95SBram Moolenaar    endif
594e2f98b95SBram Moolenaar
595eb3593b3SBram Moolenaar    if g:loaded_dbext > 300
596e2f98b95SBram Moolenaar        exec 'DBSetOption use_tbl_alias='.saveSettingAlias
597e2f98b95SBram Moolenaar    endif
598e2f98b95SBram Moolenaar
599*f193fffdSBram Moolenaar    " If the user has asked for a comma separate list of column
600*f193fffdSBram Moolenaar    " values, ask the user if they want to prepend each column
601*f193fffdSBram Moolenaar    " with a tablename alias.
602e2f98b95SBram Moolenaar    if a:list_type == 'csv' && !empty(table_cols)
603e2f98b95SBram Moolenaar        let cols       = join(table_cols, ', ')
604e2f98b95SBram Moolenaar        let cols       = s:SQLCAddAlias(table_name, table_alias, cols)
605e2f98b95SBram Moolenaar        let table_cols = [cols]
606e2f98b95SBram Moolenaar    endif
607e2f98b95SBram Moolenaar
608e2f98b95SBram Moolenaar    return table_cols
609e2f98b95SBram Moolenaarendfunction
610e2f98b95SBram Moolenaar
611