1e2f98b95SBram Moolenaar" Vim completion script
2e2f98b95SBram Moolenaar" Language:    SQL
3e2f98b95SBram Moolenaar" Maintainer:  David Fishburn <[email protected]>
4*eb3593b3SBram Moolenaar" Version:     3.0
5*eb3593b3SBram Moolenaar" Last Change: Thu Apr 20 2006 8:47:12 PM
6e2f98b95SBram Moolenaar
7e2f98b95SBram Moolenaar" Set completion with CTRL-X CTRL-O to autoloaded function.
8e2f98b95SBram Moolenaar" This check is in place in case this script is
9e2f98b95SBram Moolenaar" sourced directly instead of using the autoload feature.
10e2f98b95SBram Moolenaarif exists('&omnifunc')
11e2f98b95SBram Moolenaar    " Do not set the option if already set since this
12e2f98b95SBram Moolenaar    " results in an E117 warning.
13e2f98b95SBram Moolenaar    if &omnifunc == ""
14e2f98b95SBram Moolenaar        setlocal omnifunc=sqlcomplete#Complete
15e2f98b95SBram Moolenaar    endif
16e2f98b95SBram Moolenaarendif
17e2f98b95SBram Moolenaar
18e2f98b95SBram Moolenaarif exists('g:loaded_sql_completion')
19e2f98b95SBram Moolenaar    finish
20e2f98b95SBram Moolenaarendif
21*eb3593b3SBram Moolenaarlet g:loaded_sql_completion = 30
22e2f98b95SBram Moolenaar
23e2f98b95SBram Moolenaar" Maintains filename of dictionary
24e2f98b95SBram Moolenaarlet s:sql_file_table        = ""
25e2f98b95SBram Moolenaarlet s:sql_file_procedure    = ""
26e2f98b95SBram Moolenaarlet s:sql_file_view         = ""
27e2f98b95SBram Moolenaar
28e2f98b95SBram Moolenaar" Define various arrays to be used for caching
29e2f98b95SBram Moolenaarlet s:tbl_name              = []
30e2f98b95SBram Moolenaarlet s:tbl_alias             = []
31e2f98b95SBram Moolenaarlet s:tbl_cols              = []
32e2f98b95SBram Moolenaarlet s:syn_list              = []
33e2f98b95SBram Moolenaarlet s:syn_value             = []
34e2f98b95SBram Moolenaar
35e2f98b95SBram Moolenaar" Used in conjunction with the syntaxcomplete plugin
36e2f98b95SBram Moolenaarlet s:save_inc              = ""
37e2f98b95SBram Moolenaarlet s:save_exc              = ""
38e2f98b95SBram Moolenaarif exists('g:omni_syntax_group_include_sql')
39e2f98b95SBram Moolenaar    let s:save_inc = g:omni_syntax_group_include_sql
40e2f98b95SBram Moolenaarendif
41e2f98b95SBram Moolenaarif exists('g:omni_syntax_group_exclude_sql')
42e2f98b95SBram Moolenaar    let s:save_exc = g:omni_syntax_group_exclude_sql
43e2f98b95SBram Moolenaarendif
44e2f98b95SBram Moolenaar
45e2f98b95SBram Moolenaar" Used with the column list
46e2f98b95SBram Moolenaarlet s:save_prev_table       = ""
47e2f98b95SBram Moolenaar
48e2f98b95SBram Moolenaar" Default the option to verify table alias
49e2f98b95SBram Moolenaarif !exists('g:omni_sql_use_tbl_alias')
50e2f98b95SBram Moolenaar    let g:omni_sql_use_tbl_alias = 'a'
51e2f98b95SBram Moolenaarendif
52910f66f9SBram Moolenaar" Default syntax items to precache
53910f66f9SBram Moolenaarif !exists('g:omni_sql_precache_syntax_groups')
54910f66f9SBram Moolenaar    let g:omni_sql_precache_syntax_groups = [
55910f66f9SBram Moolenaar                \ 'syntax',
56910f66f9SBram Moolenaar                \ 'sqlKeyword',
57910f66f9SBram Moolenaar                \ 'sqlFunction',
58910f66f9SBram Moolenaar                \ 'sqlOption',
59910f66f9SBram Moolenaar                \ 'sqlType',
60910f66f9SBram Moolenaar                \ 'sqlStatement'
61910f66f9SBram Moolenaar                \ ]
62910f66f9SBram Moolenaarendif
63*eb3593b3SBram Moolenaar" Set ignorecase to the ftplugin standard
64*eb3593b3SBram Moolenaarif !exists('g:omni_sql_ignorecase')
65*eb3593b3SBram Moolenaar    let g:omni_sql_ignorecase = &ignorecase
66*eb3593b3SBram Moolenaarendif
67*eb3593b3SBram Moolenaar" During table completion, should the table list also
68*eb3593b3SBram Moolenaar" include the owner name
69*eb3593b3SBram Moolenaarif !exists('g:omni_sql_include_owner')
70*eb3593b3SBram Moolenaar    let g:omni_sql_include_owner = 0
71*eb3593b3SBram Moolenaar    if exists('g:loaded_dbext')
72*eb3593b3SBram Moolenaar        if g:loaded_dbext >= 300
73*eb3593b3SBram Moolenaar            " New to dbext 3.00, by default the table lists include the owner
74*eb3593b3SBram Moolenaar            " name of the table.  This is used when determining how much of
75*eb3593b3SBram Moolenaar            " whatever has been typed should be replaced as part of the
76*eb3593b3SBram Moolenaar            " code replacement.
77*eb3593b3SBram Moolenaar            let g:omni_sql_include_owner = 1
78*eb3593b3SBram Moolenaar        endif
79*eb3593b3SBram Moolenaar    endif
80*eb3593b3SBram Moolenaarendif
81e2f98b95SBram Moolenaar
82e2f98b95SBram Moolenaar" This function is used for the 'omnifunc' option.
83e2f98b95SBram Moolenaarfunction! sqlcomplete#Complete(findstart, base)
84e2f98b95SBram Moolenaar
85e2f98b95SBram Moolenaar    " Default to table name completion
86e2f98b95SBram Moolenaar    let compl_type = 'table'
87e2f98b95SBram Moolenaar    " Allow maps to specify what type of object completion they want
88e2f98b95SBram Moolenaar    if exists('b:sql_compl_type')
89e2f98b95SBram Moolenaar        let compl_type = b:sql_compl_type
90e2f98b95SBram Moolenaar    endif
91e2f98b95SBram Moolenaar
92910f66f9SBram Moolenaar    " First pass through this function determines how much of the line should
93910f66f9SBram Moolenaar    " be replaced by whatever is chosen from the completion list
94e2f98b95SBram Moolenaar    if a:findstart
95e2f98b95SBram Moolenaar        " Locate the start of the item, including "."
96e2f98b95SBram Moolenaar        let line = getline('.')
97e2f98b95SBram Moolenaar        let start = col('.') - 1
98e2f98b95SBram Moolenaar        let lastword = -1
99e2f98b95SBram Moolenaar        while start > 0
100e2f98b95SBram Moolenaar            if line[start - 1] =~ '\w'
101e2f98b95SBram Moolenaar                let start -= 1
102*eb3593b3SBram Moolenaar            elseif line[start - 1] =~ '\.' &&
103*eb3593b3SBram Moolenaar                        \ compl_type =~ 'column\|table\|view\|procedure'
104*eb3593b3SBram Moolenaar                " If lastword has already been set for column completion
105*eb3593b3SBram Moolenaar                " break from the loop, since we do not also want to pickup
106*eb3593b3SBram Moolenaar                " a table name if it was also supplied.
107*eb3593b3SBram Moolenaar                if lastword != -1 && compl_type =~ 'column'
108*eb3593b3SBram Moolenaar                    break
109*eb3593b3SBram Moolenaar                endif
110*eb3593b3SBram Moolenaar                " Assume we are looking for column completion
111*eb3593b3SBram Moolenaar                " column_type can be either 'column' or 'column_csv'
112*eb3593b3SBram Moolenaar                if lastword == -1 && compl_type =~ 'column'
113e2f98b95SBram Moolenaar                    let lastword = start
114e2f98b95SBram Moolenaar                endif
115*eb3593b3SBram Moolenaar                " If omni_sql_include_owner = 0, do not include the table
116*eb3593b3SBram Moolenaar                " name as part of the substitution, so break here
117*eb3593b3SBram Moolenaar                if lastword == -1 &&
118*eb3593b3SBram Moolenaar                            \ compl_type =~ 'table\|view\|procedure' &&
119*eb3593b3SBram Moolenaar                            \ g:omni_sql_include_owner == 0
120*eb3593b3SBram Moolenaar                    let lastword = start
121*eb3593b3SBram Moolenaar                    break
122*eb3593b3SBram Moolenaar                endif
123e2f98b95SBram Moolenaar                let start -= 1
124e2f98b95SBram Moolenaar            else
125e2f98b95SBram Moolenaar                break
126e2f98b95SBram Moolenaar            endif
127e2f98b95SBram Moolenaar        endwhile
128e2f98b95SBram Moolenaar
129e2f98b95SBram Moolenaar        " Return the column of the last word, which is going to be changed.
130e2f98b95SBram Moolenaar        " Remember the text that comes before it in s:prepended.
131e2f98b95SBram Moolenaar        if lastword == -1
132e2f98b95SBram Moolenaar            let s:prepended = ''
133e2f98b95SBram Moolenaar            return start
134e2f98b95SBram Moolenaar        endif
135e2f98b95SBram Moolenaar        let s:prepended = strpart(line, start, lastword - start)
136e2f98b95SBram Moolenaar        return lastword
137e2f98b95SBram Moolenaar    endif
138e2f98b95SBram Moolenaar
139910f66f9SBram Moolenaar    " Second pass through this function will determine what data to put inside
140910f66f9SBram Moolenaar    " of the completion list
141910f66f9SBram Moolenaar    " s:prepended is set by the first pass
142e2f98b95SBram Moolenaar    let base = s:prepended . a:base
143e2f98b95SBram Moolenaar
144910f66f9SBram Moolenaar    " Default the completion list to an empty list
145e2f98b95SBram Moolenaar    let compl_list = []
146e2f98b95SBram Moolenaar
147e2f98b95SBram Moolenaar    " Default to table name completion
148e2f98b95SBram Moolenaar    let compl_type = 'table'
149e2f98b95SBram Moolenaar    " Allow maps to specify what type of object completion they want
150e2f98b95SBram Moolenaar    if exists('b:sql_compl_type')
151e2f98b95SBram Moolenaar        let compl_type = b:sql_compl_type
152e2f98b95SBram Moolenaar        unlet b:sql_compl_type
153e2f98b95SBram Moolenaar    endif
154e2f98b95SBram Moolenaar
155e2f98b95SBram Moolenaar    if compl_type == 'tableReset'
156e2f98b95SBram Moolenaar        let compl_type = 'table'
157e2f98b95SBram Moolenaar        let base = ''
158e2f98b95SBram Moolenaar    endif
159e2f98b95SBram Moolenaar
160e2f98b95SBram Moolenaar    if compl_type == 'table' ||
161e2f98b95SBram Moolenaar                \ compl_type == 'procedure' ||
162e2f98b95SBram Moolenaar                \ compl_type == 'view'
163e2f98b95SBram Moolenaar
164e2f98b95SBram Moolenaar        " This type of completion relies upon the dbext.vim plugin
165e2f98b95SBram Moolenaar        if s:SQLCCheck4dbext() == -1
166e2f98b95SBram Moolenaar            return []
167e2f98b95SBram Moolenaar        endif
168e2f98b95SBram Moolenaar
169e2f98b95SBram Moolenaar        if s:sql_file_{compl_type} == ""
170e2f98b95SBram Moolenaar            let compl_type = substitute(compl_type, '\w\+', '\u&', '')
171e2f98b95SBram Moolenaar            let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type)
172e2f98b95SBram Moolenaar        endif
173e2f98b95SBram Moolenaar        let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type)
174e2f98b95SBram Moolenaar        if s:sql_file_{compl_type} != ""
175e2f98b95SBram Moolenaar            if filereadable(s:sql_file_{compl_type})
176e2f98b95SBram Moolenaar                let compl_list = readfile(s:sql_file_{compl_type})
177*eb3593b3SBram Moolenaar                " let dic_list = readfile(s:sql_file_{compl_type})
178*eb3593b3SBram Moolenaar                " if !empty(dic_list)
179*eb3593b3SBram Moolenaar                "     for elem in dic_list
180*eb3593b3SBram Moolenaar                "         let kind = (compl_type=='table'?'m':(compl_type=='procedure'?'f':'v'))
181*eb3593b3SBram Moolenaar                "         let item = {'word':elem, 'menu':elem, 'kind':kind, 'info':compl_type}
182*eb3593b3SBram Moolenaar                "         let compl_list += [item]
183*eb3593b3SBram Moolenaar                "     endfor
184*eb3593b3SBram Moolenaar                " endif
185e2f98b95SBram Moolenaar            endif
186e2f98b95SBram Moolenaar        endif
187e2f98b95SBram Moolenaar    elseif compl_type == 'column'
188e2f98b95SBram Moolenaar
189e2f98b95SBram Moolenaar        " This type of completion relies upon the dbext.vim plugin
190e2f98b95SBram Moolenaar        if s:SQLCCheck4dbext() == -1
191e2f98b95SBram Moolenaar            return []
192e2f98b95SBram Moolenaar        endif
193e2f98b95SBram Moolenaar
194e2f98b95SBram Moolenaar        if base == ""
195e2f98b95SBram Moolenaar            " The last time we displayed a column list we stored
196e2f98b95SBram Moolenaar            " the table name.  If the user selects a column list
197e2f98b95SBram Moolenaar            " without a table name of alias present, assume they want
198e2f98b95SBram Moolenaar            " the previous column list displayed.
199e2f98b95SBram Moolenaar            let base = s:save_prev_table
200e2f98b95SBram Moolenaar        endif
201e2f98b95SBram Moolenaar
202e2f98b95SBram Moolenaar        if base != ""
203e2f98b95SBram Moolenaar            let compl_list        = s:SQLCGetColumns(base, '')
204e2f98b95SBram Moolenaar            let s:save_prev_table = base
205e2f98b95SBram Moolenaar            let base              = ''
206e2f98b95SBram Moolenaar        endif
207e2f98b95SBram Moolenaar    elseif compl_type == 'column_csv'
208e2f98b95SBram Moolenaar
209e2f98b95SBram Moolenaar        " This type of completion relies upon the dbext.vim plugin
210e2f98b95SBram Moolenaar        if s:SQLCCheck4dbext() == -1
211e2f98b95SBram Moolenaar            return []
212e2f98b95SBram Moolenaar        endif
213e2f98b95SBram Moolenaar
214e2f98b95SBram Moolenaar        if base == ""
215e2f98b95SBram Moolenaar            " The last time we displayed a column list we stored
216e2f98b95SBram Moolenaar            " the table name.  If the user selects a column list
217e2f98b95SBram Moolenaar            " without a table name of alias present, assume they want
218e2f98b95SBram Moolenaar            " the previous column list displayed.
219e2f98b95SBram Moolenaar            let base = s:save_prev_table
220e2f98b95SBram Moolenaar        endif
221e2f98b95SBram Moolenaar
222e2f98b95SBram Moolenaar        if base != ""
223e2f98b95SBram Moolenaar            let compl_list        = s:SQLCGetColumns(base, 'csv')
224e2f98b95SBram Moolenaar            let s:save_prev_table = base
225e2f98b95SBram Moolenaar            " Join the column array into 1 single element array
226e2f98b95SBram Moolenaar            " but make the columns column separated
227e2f98b95SBram Moolenaar            let compl_list        = [join(compl_list, ', ')]
228e2f98b95SBram Moolenaar            let base              = ''
229e2f98b95SBram Moolenaar        endif
230e2f98b95SBram Moolenaar    elseif compl_type == 'resetCache'
231e2f98b95SBram Moolenaar        " Reset all cached items
232e2f98b95SBram Moolenaar        let s:tbl_name  = []
233e2f98b95SBram Moolenaar        let s:tbl_alias = []
234e2f98b95SBram Moolenaar        let s:tbl_cols  = []
235e2f98b95SBram Moolenaar        let s:syn_list  = []
236e2f98b95SBram Moolenaar        let s:syn_value = []
237e2f98b95SBram Moolenaar    else
238910f66f9SBram Moolenaar        let compl_list = s:SQLCGetSyntaxList(compl_type)
239e2f98b95SBram Moolenaar    endif
240e2f98b95SBram Moolenaar
241e2f98b95SBram Moolenaar    if base != ''
242e2f98b95SBram Moolenaar        " Filter the list based on the first few characters the user
243e2f98b95SBram Moolenaar        " entered
244*eb3593b3SBram Moolenaar        let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "^'.base.'"'
245*eb3593b3SBram Moolenaar        let compl_list = filter(deepcopy(compl_list), expr)
246e2f98b95SBram Moolenaar    endif
247e2f98b95SBram Moolenaar
248910f66f9SBram Moolenaar    if exists('b:sql_compl_savefunc') && b:sql_compl_savefunc != ""
249910f66f9SBram Moolenaar        let &omnifunc = b:sql_compl_savefunc
250910f66f9SBram Moolenaar    endif
251910f66f9SBram Moolenaar
252e2f98b95SBram Moolenaar    return compl_list
253e2f98b95SBram Moolenaarendfunc
254e2f98b95SBram Moolenaar
255e2f98b95SBram Moolenaarfunction! s:SQLCWarningMsg(msg)
256e2f98b95SBram Moolenaar    echohl WarningMsg
257e2f98b95SBram Moolenaar    echomsg a:msg
258e2f98b95SBram Moolenaar    echohl None
259e2f98b95SBram Moolenaarendfunction
260e2f98b95SBram Moolenaar
261e2f98b95SBram Moolenaarfunction! s:SQLCErrorMsg(msg)
262e2f98b95SBram Moolenaar    echohl ErrorMsg
263e2f98b95SBram Moolenaar    echomsg a:msg
264e2f98b95SBram Moolenaar    echohl None
265e2f98b95SBram Moolenaarendfunction
266e2f98b95SBram Moolenaar
267910f66f9SBram Moolenaarfunction! sqlcomplete#PreCacheSyntax(...)
268910f66f9SBram Moolenaar    let syn_group_arr = []
269910f66f9SBram Moolenaar    if a:0 > 0
270910f66f9SBram Moolenaar        let syn_group_arr = a:1
271910f66f9SBram Moolenaar    else
272910f66f9SBram Moolenaar        let syn_group_arr = g:omni_sql_precache_syntax_groups
273910f66f9SBram Moolenaar    endif
274910f66f9SBram Moolenaar    if !empty(syn_group_arr)
275910f66f9SBram Moolenaar        for group_name in syn_group_arr
276910f66f9SBram Moolenaar            call s:SQLCGetSyntaxList(group_name)
277910f66f9SBram Moolenaar        endfor
278910f66f9SBram Moolenaar    endif
279910f66f9SBram Moolenaarendfunction
280910f66f9SBram Moolenaar
281910f66f9SBram Moolenaarfunction! sqlcomplete#Map(type)
282910f66f9SBram Moolenaar    " Tell the SQL plugin what you want to complete
283910f66f9SBram Moolenaar    let b:sql_compl_type=a:type
284910f66f9SBram Moolenaar    " Record previous omnifunc, if the SQL completion
285910f66f9SBram Moolenaar    " is being used in conjunction with other filetype
286910f66f9SBram Moolenaar    " completion plugins
287910f66f9SBram Moolenaar    if &omnifunc != "" && &omnifunc != 'sqlcomplete#Complete'
288910f66f9SBram Moolenaar        " Record the previous omnifunc, the plugin
289910f66f9SBram Moolenaar        " will automatically set this back so that it
290910f66f9SBram Moolenaar        " does not interfere with other ftplugins settings
291910f66f9SBram Moolenaar        let b:sql_compl_savefunc=&omnifunc
292910f66f9SBram Moolenaar    endif
293910f66f9SBram Moolenaar    " Set the OMNI func for the SQL completion plugin
294910f66f9SBram Moolenaar    let &omnifunc='sqlcomplete#Complete'
295910f66f9SBram Moolenaarendfunction
296910f66f9SBram Moolenaar
297910f66f9SBram Moolenaarfunction! s:SQLCGetSyntaxList(syn_group)
298910f66f9SBram Moolenaar    let syn_group  = a:syn_group
299910f66f9SBram Moolenaar    let compl_list = []
300910f66f9SBram Moolenaar
301910f66f9SBram Moolenaar    " Check if we have already cached the syntax list
302910f66f9SBram Moolenaar    let list_idx = index(s:syn_list, syn_group, 0, &ignorecase)
303910f66f9SBram Moolenaar    if list_idx > -1
304910f66f9SBram Moolenaar        " Return previously cached value
305910f66f9SBram Moolenaar        let compl_list = s:syn_value[list_idx]
306910f66f9SBram Moolenaar    else
307910f66f9SBram Moolenaar        " Request the syntax list items from the
308910f66f9SBram Moolenaar        " syntax completion plugin
309910f66f9SBram Moolenaar        if syn_group == 'syntax'
310910f66f9SBram Moolenaar            " Handle this special case.  This allows the user
311910f66f9SBram Moolenaar            " to indicate they want all the syntax items available,
312910f66f9SBram Moolenaar            " so do not specify a specific include list.
313910f66f9SBram Moolenaar            let g:omni_syntax_group_include_sql = ''
314910f66f9SBram Moolenaar        else
315910f66f9SBram Moolenaar            " The user has specified a specific syntax group
316910f66f9SBram Moolenaar            let g:omni_syntax_group_include_sql = syn_group
317910f66f9SBram Moolenaar        endif
318910f66f9SBram Moolenaar        let g:omni_syntax_group_exclude_sql = ''
319910f66f9SBram Moolenaar        let syn_value                       = OmniSyntaxList()
320910f66f9SBram Moolenaar        let g:omni_syntax_group_include_sql = s:save_inc
321910f66f9SBram Moolenaar        let g:omni_syntax_group_exclude_sql = s:save_exc
322910f66f9SBram Moolenaar        " Cache these values for later use
323910f66f9SBram Moolenaar        let s:syn_list  = add( s:syn_list,  syn_group )
324910f66f9SBram Moolenaar        let s:syn_value = add( s:syn_value, syn_value )
325910f66f9SBram Moolenaar        let compl_list  = syn_value
326910f66f9SBram Moolenaar    endif
327910f66f9SBram Moolenaar
328910f66f9SBram Moolenaar    return compl_list
329910f66f9SBram Moolenaarendfunction
330910f66f9SBram Moolenaar
331e2f98b95SBram Moolenaarfunction! s:SQLCCheck4dbext()
332e2f98b95SBram Moolenaar    if !exists('g:loaded_dbext')
333e2f98b95SBram Moolenaar        let msg = "The dbext plugin must be loaded for dynamic SQL completion"
334e2f98b95SBram Moolenaar        call s:SQLCErrorMsg(msg)
335e2f98b95SBram Moolenaar        " Leave time for the user to read the error message
336e2f98b95SBram Moolenaar        :sleep 2
337e2f98b95SBram Moolenaar        return -1
338*eb3593b3SBram Moolenaar    elseif g:loaded_dbext < 300
339*eb3593b3SBram Moolenaar        let msg = "The dbext plugin must be at least version 3.00 " .
340e2f98b95SBram Moolenaar                    \ " for dynamic SQL completion"
341e2f98b95SBram Moolenaar        call s:SQLCErrorMsg(msg)
342e2f98b95SBram Moolenaar        " Leave time for the user to read the error message
343e2f98b95SBram Moolenaar        :sleep 2
344e2f98b95SBram Moolenaar        return -1
345e2f98b95SBram Moolenaar    endif
346e2f98b95SBram Moolenaar    return 1
347e2f98b95SBram Moolenaarendfunction
348e2f98b95SBram Moolenaar
349e2f98b95SBram Moolenaarfunction! s:SQLCAddAlias(table_name, table_alias, cols)
350e2f98b95SBram Moolenaar    let table_name  = a:table_name
351e2f98b95SBram Moolenaar    let table_alias = a:table_alias
352e2f98b95SBram Moolenaar    let cols        = a:cols
353e2f98b95SBram Moolenaar
354e2f98b95SBram Moolenaar    if g:omni_sql_use_tbl_alias != 'n'
355e2f98b95SBram Moolenaar        if table_alias == ''
356e2f98b95SBram Moolenaar            if 'da' =~? g:omni_sql_use_tbl_alias
357e2f98b95SBram Moolenaar                if table_name =~ '_'
358e2f98b95SBram Moolenaar                    " Treat _ as separators since people often use these
359e2f98b95SBram Moolenaar                    " for word separators
360e2f98b95SBram Moolenaar                    let save_keyword = &iskeyword
361e2f98b95SBram Moolenaar                    setlocal iskeyword-=_
362e2f98b95SBram Moolenaar
363e2f98b95SBram Moolenaar                    " Get the first letter of each word
364e2f98b95SBram Moolenaar                    " [[:alpha:]] is used instead of \w
365e2f98b95SBram Moolenaar                    " to catch extended accented characters
366e2f98b95SBram Moolenaar                    "
367e2f98b95SBram Moolenaar                    let table_alias = substitute(
368e2f98b95SBram Moolenaar                                \ table_name,
369e2f98b95SBram Moolenaar                                \ '\<[[:alpha:]]\+\>_\?',
370e2f98b95SBram Moolenaar                                \ '\=strpart(submatch(0), 0, 1)',
371e2f98b95SBram Moolenaar                                \ 'g'
372e2f98b95SBram Moolenaar                                \ )
373e2f98b95SBram Moolenaar                    " Restore original value
374e2f98b95SBram Moolenaar                    let &iskeyword = save_keyword
375e2f98b95SBram Moolenaar                elseif table_name =~ '\u\U'
376e2f98b95SBram Moolenaar                    let initials = substitute(
377e2f98b95SBram Moolenaar                                \ table_name, '\(\u\)\U*', '\1', 'g')
378e2f98b95SBram Moolenaar                else
379e2f98b95SBram Moolenaar                    let table_alias = strpart(table_name, 0, 1)
380e2f98b95SBram Moolenaar                endif
381e2f98b95SBram Moolenaar            endif
382e2f98b95SBram Moolenaar        endif
383e2f98b95SBram Moolenaar        if table_alias != ''
384e2f98b95SBram Moolenaar            " Following a word character, make sure there is a . and no spaces
385e2f98b95SBram Moolenaar            let table_alias = substitute(table_alias, '\w\zs\.\?\s*$', '.', '')
386e2f98b95SBram Moolenaar            if 'a' =~? g:omni_sql_use_tbl_alias && a:table_alias == ''
387e2f98b95SBram Moolenaar                let table_alias = inputdialog("Enter table alias:", table_alias)
388e2f98b95SBram Moolenaar            endif
389e2f98b95SBram Moolenaar        endif
390e2f98b95SBram Moolenaar        if table_alias != ''
391e2f98b95SBram Moolenaar            let cols = substitute(cols, '\<\w', table_alias.'&', 'g')
392e2f98b95SBram Moolenaar        endif
393e2f98b95SBram Moolenaar    endif
394e2f98b95SBram Moolenaar
395e2f98b95SBram Moolenaar    return cols
396e2f98b95SBram Moolenaarendfunction
397e2f98b95SBram Moolenaar
398e2f98b95SBram Moolenaarfunction! s:SQLCGetColumns(table_name, list_type)
399e2f98b95SBram Moolenaar    let table_name   = matchstr(a:table_name, '^\w\+')
400e2f98b95SBram Moolenaar    let table_cols   = []
401e2f98b95SBram Moolenaar    let table_alias  = ''
402e2f98b95SBram Moolenaar    let move_to_top  = 1
403e2f98b95SBram Moolenaar
404*eb3593b3SBram Moolenaar    if g:loaded_dbext >= 300
405e2f98b95SBram Moolenaar        let saveSettingAlias = DB_listOption('use_tbl_alias')
406e2f98b95SBram Moolenaar        exec 'DBSetOption use_tbl_alias=n'
407e2f98b95SBram Moolenaar    endif
408e2f98b95SBram Moolenaar
409e2f98b95SBram Moolenaar    " Check if we have already cached the column list for this table
410e2f98b95SBram Moolenaar    " by its name
411e2f98b95SBram Moolenaar    let list_idx = index(s:tbl_name, table_name, 0, &ignorecase)
412e2f98b95SBram Moolenaar    if list_idx > -1
413e2f98b95SBram Moolenaar        let table_cols = split(s:tbl_cols[list_idx])
414e2f98b95SBram Moolenaar    else
415e2f98b95SBram Moolenaar        " Check if we have already cached the column list for this table
416e2f98b95SBram Moolenaar        " by its alias, assuming the table_name provided was actually
417e2f98b95SBram Moolenaar        " the alias for the table instead
418e2f98b95SBram Moolenaar        "     select *
419e2f98b95SBram Moolenaar        "       from area a
420e2f98b95SBram Moolenaar        "      where a.
421e2f98b95SBram Moolenaar        let list_idx = index(s:tbl_alias, table_name, 0, &ignorecase)
422e2f98b95SBram Moolenaar        if list_idx > -1
423e2f98b95SBram Moolenaar            let table_alias = table_name
424e2f98b95SBram Moolenaar            let table_name  = s:tbl_name[list_idx]
425e2f98b95SBram Moolenaar            let table_cols  = split(s:tbl_cols[list_idx])
426e2f98b95SBram Moolenaar        endif
427e2f98b95SBram Moolenaar    endif
428e2f98b95SBram Moolenaar
429e2f98b95SBram Moolenaar    " If we have not found a cached copy of the table
430e2f98b95SBram Moolenaar    " And the table ends in a "." or we are looking for a column list
431e2f98b95SBram Moolenaar    " if list_idx == -1 && (a:table_name =~ '\.' || b:sql_compl_type =~ 'column')
432e2f98b95SBram Moolenaar    " if list_idx == -1 && (a:table_name =~ '\.' || a:list_type =~ 'csv')
433e2f98b95SBram Moolenaar    if list_idx == -1
434e2f98b95SBram Moolenaar         let saveY      = @y
435e2f98b95SBram Moolenaar         let saveSearch = @/
436e2f98b95SBram Moolenaar         let saveWScan  = &wrapscan
437e2f98b95SBram Moolenaar         let curline    = line(".")
438e2f98b95SBram Moolenaar         let curcol     = col(".")
439e2f98b95SBram Moolenaar
440e2f98b95SBram Moolenaar         " Do not let searchs wrap
441e2f98b95SBram Moolenaar         setlocal nowrapscan
442e2f98b95SBram Moolenaar         " If . was entered, look at the word just before the .
443e2f98b95SBram Moolenaar         " We are looking for something like this:
444e2f98b95SBram Moolenaar         "    select *
445e2f98b95SBram Moolenaar         "      from customer c
446e2f98b95SBram Moolenaar         "     where c.
447e2f98b95SBram Moolenaar         " So when . is pressed, we need to find 'c'
448e2f98b95SBram Moolenaar         "
449e2f98b95SBram Moolenaar
450e2f98b95SBram Moolenaar         " Search backwards to the beginning of the statement
451e2f98b95SBram Moolenaar         " and do NOT wrap
452e2f98b95SBram Moolenaar         " exec 'silent! normal! v?\<\(select\|update\|delete\|;\)\>'."\n".'"yy'
453e2f98b95SBram Moolenaar         exec 'silent! normal! ?\<\(select\|update\|delete\|;\)\>'."\n"
454e2f98b95SBram Moolenaar
455e2f98b95SBram Moolenaar         " Start characterwise visual mode
456e2f98b95SBram Moolenaar         " Advance right one character
457e2f98b95SBram Moolenaar         " Search foward until one of the following:
458e2f98b95SBram Moolenaar         "     1.  Another select/update/delete statement
459e2f98b95SBram Moolenaar         "     2.  A ; at the end of a line (the delimiter)
460e2f98b95SBram Moolenaar         "     3.  The end of the file (incase no delimiter)
461e2f98b95SBram Moolenaar         " Yank the visually selected text into the "y register.
462e2f98b95SBram Moolenaar         exec 'silent! normal! vl/\(\<select\>\|\<update\>\|\<delete\>\|;\s*$\|\%$\)'."\n".'"yy'
463e2f98b95SBram Moolenaar
464e2f98b95SBram Moolenaar         let query = @y
465e2f98b95SBram Moolenaar         let query = substitute(query, "\n", ' ', 'g')
466e2f98b95SBram Moolenaar         let found = 0
467e2f98b95SBram Moolenaar
468e2f98b95SBram Moolenaar         " if query =~? '^\(select\|update\|delete\)'
469e2f98b95SBram Moolenaar         if query =~? '^\(select\)'
470e2f98b95SBram Moolenaar             let found = 1
471e2f98b95SBram Moolenaar             "  \(\(\<\w\+\>\)\.\)\?   -
472e2f98b95SBram Moolenaar             " 'from.\{-}'  - Starting at the from clause
473e2f98b95SBram Moolenaar             " '\zs\(\(\<\w\+\>\)\.\)\?' - Get the owner name (optional)
474e2f98b95SBram Moolenaar             " '\<\w\+\>\ze' - Get the table name
475e2f98b95SBram Moolenaar             " '\s\+\<'.table_name.'\>' - Followed by the alias
476e2f98b95SBram Moolenaar             " '\s*\.\@!.*'  - Cannot be followed by a .
477e2f98b95SBram Moolenaar             " '\(\<where\>\|$\)' - Must be followed by a WHERE clause
478e2f98b95SBram Moolenaar             " '.*'  - Exclude the rest of the line in the match
479e2f98b95SBram Moolenaar             let table_name_new = matchstr(@y,
480e2f98b95SBram Moolenaar                         \ 'from.\{-}'.
481e2f98b95SBram Moolenaar                         \ '\zs\(\(\<\w\+\>\)\.\)\?'.
482e2f98b95SBram Moolenaar                         \ '\<\w\+\>\ze'.
483e2f98b95SBram Moolenaar                         \ '\s\+\%(as\s\+\)\?\<'.table_name.'\>'.
484e2f98b95SBram Moolenaar                         \ '\s*\.\@!.*'.
485e2f98b95SBram Moolenaar                         \ '\(\<where\>\|$\)'.
486e2f98b95SBram Moolenaar                         \ '.*'
487e2f98b95SBram Moolenaar                         \ )
488e2f98b95SBram Moolenaar             if table_name_new != ''
489e2f98b95SBram Moolenaar                 let table_alias = table_name
490e2f98b95SBram Moolenaar                 let table_name  = table_name_new
491e2f98b95SBram Moolenaar
492e2f98b95SBram Moolenaar                 let list_idx = index(s:tbl_name, table_name, 0, &ignorecase)
493e2f98b95SBram Moolenaar                 if list_idx > -1
494e2f98b95SBram Moolenaar                     let table_cols  = split(s:tbl_cols[list_idx])
495e2f98b95SBram Moolenaar                     let s:tbl_name[list_idx]  = table_name
496e2f98b95SBram Moolenaar                     let s:tbl_alias[list_idx] = table_alias
497e2f98b95SBram Moolenaar                 else
498e2f98b95SBram Moolenaar                     let list_idx = index(s:tbl_alias, table_name, 0, &ignorecase)
499e2f98b95SBram Moolenaar                     if list_idx > -1
500e2f98b95SBram Moolenaar                         let table_cols = split(s:tbl_cols[list_idx])
501e2f98b95SBram Moolenaar                         let s:tbl_name[list_idx]  = table_name
502e2f98b95SBram Moolenaar                         let s:tbl_alias[list_idx] = table_alias
503e2f98b95SBram Moolenaar                     endif
504e2f98b95SBram Moolenaar                 endif
505e2f98b95SBram Moolenaar
506e2f98b95SBram Moolenaar             endif
507e2f98b95SBram Moolenaar         else
508e2f98b95SBram Moolenaar             " Simply assume it is a table name provided with a . on the end
509e2f98b95SBram Moolenaar             let found = 1
510e2f98b95SBram Moolenaar         endif
511e2f98b95SBram Moolenaar
512e2f98b95SBram Moolenaar         let @y        = saveY
513e2f98b95SBram Moolenaar         let @/        = saveSearch
514e2f98b95SBram Moolenaar         let &wrapscan = saveWScan
515e2f98b95SBram Moolenaar
516e2f98b95SBram Moolenaar         " Return to previous location
517e2f98b95SBram Moolenaar         call cursor(curline, curcol)
518e2f98b95SBram Moolenaar
519e2f98b95SBram Moolenaar         if found == 0
520*eb3593b3SBram Moolenaar             if g:loaded_dbext > 300
521e2f98b95SBram Moolenaar                 exec 'DBSetOption use_tbl_alias='.saveSettingAlias
522e2f98b95SBram Moolenaar             endif
523e2f98b95SBram Moolenaar
524e2f98b95SBram Moolenaar             " Not a SQL statement, do not display a list
525e2f98b95SBram Moolenaar             return []
526e2f98b95SBram Moolenaar         endif
527e2f98b95SBram Moolenaar    endif
528e2f98b95SBram Moolenaar
529e2f98b95SBram Moolenaar    if empty(table_cols)
530e2f98b95SBram Moolenaar        " Specify silent mode, no messages to the user (tbl, 1)
531e2f98b95SBram Moolenaar        " Specify do not comma separate (tbl, 1, 1)
532e2f98b95SBram Moolenaar        let table_cols_str = DB_getListColumn(table_name, 1, 1)
533e2f98b95SBram Moolenaar
534e2f98b95SBram Moolenaar        if table_cols_str != ""
535e2f98b95SBram Moolenaar            let s:tbl_name  = add( s:tbl_name,  table_name )
536e2f98b95SBram Moolenaar            let s:tbl_alias = add( s:tbl_alias, table_alias )
537e2f98b95SBram Moolenaar            let s:tbl_cols  = add( s:tbl_cols,  table_cols_str )
538e2f98b95SBram Moolenaar            let table_cols  = split(table_cols_str)
539e2f98b95SBram Moolenaar        endif
540e2f98b95SBram Moolenaar
541e2f98b95SBram Moolenaar    endif
542e2f98b95SBram Moolenaar
543*eb3593b3SBram Moolenaar    if g:loaded_dbext > 300
544e2f98b95SBram Moolenaar        exec 'DBSetOption use_tbl_alias='.saveSettingAlias
545e2f98b95SBram Moolenaar    endif
546e2f98b95SBram Moolenaar
547e2f98b95SBram Moolenaar    if a:list_type == 'csv' && !empty(table_cols)
548e2f98b95SBram Moolenaar        let cols = join(table_cols, ', ')
549e2f98b95SBram Moolenaar        let cols = s:SQLCAddAlias(table_name, table_alias, cols)
550e2f98b95SBram Moolenaar        let table_cols = [cols]
551e2f98b95SBram Moolenaar    endif
552e2f98b95SBram Moolenaar
553e2f98b95SBram Moolenaar    return table_cols
554e2f98b95SBram Moolenaarendfunction
555e2f98b95SBram Moolenaar
556