blob: 5599c0d03eec566ca1f8ac05a4920e19c9e92f24 [file] [log] [blame]
Bram Moolenaarf193fff2006-04-27 00:02:13 +00001" Vim OMNI completion script for SQL
Bram Moolenaare2f98b92006-03-29 21:18:24 +00002" Language: SQL
Bram Moolenaar5c736222010-01-06 20:54:52 +01003" Maintainer: David Fishburn <dfishburn dot vim at gmail dot com>
Bram Moolenaarf9d5ca12010-08-01 16:13:51 +02004" Version: 10.0
5" Last Change: 2010 Jun 11
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006" Usage: For detailed help
7" ":help sql.txt"
8" or ":help ft-sql-omni"
9" or read $VIMRUNTIME/doc/sql.txt
Bram Moolenaare2f98b92006-03-29 21:18:24 +000010
Bram Moolenaar5c736222010-01-06 20:54:52 +010011" History
Bram Moolenaarf9d5ca12010-08-01 16:13:51 +020012" 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"
Bram Moolenaar00a927d2010-05-14 23:24:24 +020028" 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.
Bram Moolenaarf9d5ca12010-08-01 16:13:51 +020034"
Bram Moolenaar00a927d2010-05-14 23:24:24 +020035" 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.
Bram Moolenaarf9d5ca12010-08-01 16:13:51 +020038"
Bram Moolenaar5c736222010-01-06 20:54:52 +010039" Version 7.0
40" Better handling of object names
Bram Moolenaarf9d5ca12010-08-01 16:13:51 +020041"
Bram Moolenaar5c736222010-01-06 20:54:52 +010042" Version 6.0
43" Supports object names with spaces "my table name"
44"
Bram Moolenaare2f98b92006-03-29 21:18:24 +000045" 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
Bram Moolenaarf9d5ca12010-08-01 16:13:51 +020059let g:loaded_sql_completion = 100
Bram Moolenaare2f98b92006-03-29 21:18:24 +000060
61" Maintains filename of dictionary
Bram Moolenaar910f66f2006-04-05 20:41:53 +000062let s:sql_file_table = ""
63let s:sql_file_procedure = ""
64let s:sql_file_view = ""
Bram Moolenaare2f98b92006-03-29 21:18:24 +000065
66" Define various arrays to be used for caching
Bram Moolenaar910f66f2006-04-05 20:41:53 +000067let s:tbl_name = []
68let s:tbl_alias = []
69let s:tbl_cols = []
70let s:syn_list = []
71let s:syn_value = []
Bram Moolenaare2f98b92006-03-29 21:18:24 +000072
73" Used in conjunction with the syntaxcomplete plugin
Bram Moolenaar910f66f2006-04-05 20:41:53 +000074let s:save_inc = ""
75let s:save_exc = ""
Bram Moolenaare2f98b92006-03-29 21:18:24 +000076if 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
Bram Moolenaar910f66f2006-04-05 20:41:53 +000084let s:save_prev_table = ""
Bram Moolenaare2f98b92006-03-29 21:18:24 +000085
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
Bram Moolenaar910f66f2006-04-05 20:41:53 +000090" 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
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000101" 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
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000119
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
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000130 " First pass through this function determines how much of the line should
131 " be replaced by whatever is chosen from the completion list
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000132 if a:findstart
133 " Locate the start of the item, including "."
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000134 let line = getline('.')
135 let start = col('.') - 1
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000136 let lastword = -1
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000137 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
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000142 while start > 0
Bram Moolenaar5c736222010-01-06 20:54:52 +0100143 " 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
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000152 let start -= 1
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000153 elseif line[start - 1] =~ '\.' &&
154 \ compl_type =~ 'column\|table\|view\|procedure'
Bram Moolenaar5c736222010-01-06 20:54:52 +0100155 " 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
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000160 " 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.
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000163 if lastword != -1 && compl_type == 'column'
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000164 break
165 endif
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000166 " 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
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000170 let lastword = start
171 endif
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000172 " 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 &&
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000175 \ compl_type =~ 'table\|view\|procedure\column_csv' &&
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000176 \ g:omni_sql_include_owner == 0
177 let lastword = start
178 break
179 endif
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000180 let start -= 1
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000181 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
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000196 " 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
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000199 let base = s:prepended . a:base
200
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000201 " Default the completion list to an empty list
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000202 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
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000226 " Allow the user to override the dbext plugin to specify whether
227 " the owner/creator should be included in the list
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000228 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')
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000231 endif
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000232
233 let compl_type_uc = substitute(compl_type, '\w\+', '\u&', '')
Bram Moolenaar5c736222010-01-06 20:54:52 +0100234 " 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
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000238 let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type_uc)
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000239 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
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000244
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000245 if g:loaded_dbext > 300
246 exec 'DBSetOption dict_show_owner='.saveSetting
247 endif
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000248 elseif compl_type =~? 'column'
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000249
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
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000263 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
Bram Moolenaar00a927d2010-05-14 23:24:24 +0200281 " and the DrillIntoTable (using <Right>) then
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000282 " 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 != ""
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000315 let s:save_prev_table = base
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000316 let list_type = ''
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000317
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000318 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
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000324
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000325 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
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000339
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000340 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
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000345 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 = []
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000353
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
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000358 else
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000359 let compl_list = s:SQLCGetSyntaxList(compl_type)
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000360 endif
361
362 if base != ''
Bram Moolenaar5c736222010-01-06 20:54:52 +0100363 " 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.'\\)"'
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000373 let compl_list = filter(deepcopy(compl_list), expr)
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000374 endif
375
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000376 if exists('b:sql_compl_savefunc') && b:sql_compl_savefunc != ""
377 let &omnifunc = b:sql_compl_savefunc
378 endif
379
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000380 return compl_list
381endfunc
382
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000383function! sqlcomplete#PreCacheSyntax(...)
384 let syn_group_arr = []
Bram Moolenaarf9d5ca12010-08-01 16:13:51 +0200385 let syn_items = []
386
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000387 if a:0 > 0
Bram Moolenaarf9d5ca12010-08-01 16:13:51 +0200388 if type(a:1) != 3
389 call s:SQLCWarningMsg("Parameter is not a list. Example:['syntaxGroup1', 'syntaxGroup2']")
390 return ''
391 endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000392 let syn_group_arr = a:1
393 else
394 let syn_group_arr = g:omni_sql_precache_syntax_groups
395 endif
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000396 " For each group specified in the list, precache all
397 " the sytnax items.
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000398 if !empty(syn_group_arr)
399 for group_name in syn_group_arr
Bram Moolenaarf9d5ca12010-08-01 16:13:51 +0200400 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
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000430 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
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000450function! 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
Bram Moolenaar00a927d2010-05-14 23:24:24 +0200456 call feedkeys("\<C-Y>\<C-X>\<C-O>", 'n')
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000457 else
Bram Moolenaar00a927d2010-05-14 23:24:24 +0200458 " 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")'
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000464 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
Bram Moolenaar00a927d2010-05-14 23:24:24 +0200475 " 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")'
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000481 endif
482 return ""
483endfunction
484
485function! s:SQLCWarningMsg(msg)
486 echohl WarningMsg
Bram Moolenaarf9d5ca12010-08-01 16:13:51 +0200487 echomsg 'SQLComplete:'.a:msg
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000488 echohl None
489endfunction
490
491function! s:SQLCErrorMsg(msg)
492 echohl ErrorMsg
Bram Moolenaarf9d5ca12010-08-01 16:13:51 +0200493 echomsg 'SQLComplete:'.a:msg
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000494 echohl None
495endfunction
496
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000497function! 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 = ''
Bram Moolenaarf9d5ca12010-08-01 16:13:51 +0200519 let syn_value = syntaxcomplete#OmniSyntaxList()
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000520 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
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000531function! 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
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000538 elseif g:loaded_dbext < 600
539 let msg = "The dbext plugin must be at least version 5.30 " .
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000540 \ " 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)
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000550 " Strip off the owner if included
551 let table_name = matchstr(a:table_name, '\%(.\{-}\.\)\?\zs\(.*\)' )
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000552 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'
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000577 let table_alias = substitute(
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000578 \ 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
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000599function! 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.
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000602 " ^ - 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\("\|\]\)\?\.' )
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000611 return owner
612endfunction
613
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000614function! s:SQLCGetColumns(table_name, list_type)
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000615 " Check if the table name was provided as part of the column name
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000616 let table_name = matchstr(a:table_name, '^["\[\]a-zA-Z0-9_ ]\+\ze\.\?')
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000617 let table_cols = []
618 let table_alias = ''
619 let move_to_top = 1
620
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000621 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\+', '', '')
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000626 if g:loaded_dbext >= 300
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000627 let saveSettingAlias = DB_listOption('use_tbl_alias')
628 exec 'DBSetOption use_tbl_alias=n'
629 endif
630
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000631 let table_name_stripped = substitute(table_name, '["\[\]]*', '', 'g')
632
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000633 " Check if we have already cached the column list for this table
634 " by its name
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000635 let list_idx = index(s:tbl_name, table_name_stripped, 0, &ignorecase)
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000636 if list_idx > -1
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000637 let table_cols = split(s:tbl_cols[list_idx], '\n')
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000638 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.
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000645 let list_idx = index(s:tbl_alias, table_name_stripped, 0, &ignorecase)
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000646 if list_idx > -1
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000647 let table_alias = table_name_stripped
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000648 let table_name = s:tbl_name[list_idx]
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000649 let table_cols = split(s:tbl_cols[list_idx], '\n')
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000650 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'
Bram Moolenaar00a927d2010-05-14 23:24:24 +0200677 exec 'silent! normal! ?\<\c\(select\|update\|delete\|;\)\>'."\n"
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000678
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.
Bram Moolenaar00a927d2010-05-14 23:24:24 +0200686 exec 'silent! normal! vl/\c\(\<select\>\|\<update\>\|\<delete\>\|;\s*$\|\%$\)'."\n".'"yy'
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000687
688 let query = @y
689 let query = substitute(query, "\n", ' ', 'g')
690 let found = 0
691
Bram Moolenaar00a927d2010-05-14 23:24:24 +0200692 " if query =~? '^\c\(select\)'
693 if query =~? '^\(select\|update\|delete\)'
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000694 let found = 1
695 " \(\(\<\w\+\>\)\.\)\? -
Bram Moolenaar00a927d2010-05-14 23:24:24 +0200696 " '\c\(from\|join\|,\).\{-}' - Starting at the from clause (case insensitive)
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000697 " '\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
Bram Moolenaar00a927d2010-05-14 23:24:24 +0200703 " 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 " \ )
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000714 let table_name_new = matchstr(@y,
Bram Moolenaar00a927d2010-05-14 23:24:24 +0200715 \ '\c\(\<from\>\|\<join\>\|,\)\s*'.
716 \ '\zs\(\("\|\[\)\?\w\+\("\|\]\)\?\.\)\?'.
717 \ '\("\|\[\)\?\w\+\("\|\]\)\?\ze'.
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000718 \ '\s\+\%(as\s\+\)\?\<'.
719 \ matchstr(table_name, '.\{-}\ze\.\?$').
720 \ '\>'.
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000721 \ '\s*\.\@!.*'.
722 \ '\(\<where\>\|$\)'.
723 \ '.*'
724 \ )
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000725
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000726 if table_name_new != ''
727 let table_alias = table_name
Bram Moolenaar00a927d2010-05-14 23:24:24 +0200728 let table_name = matchstr( table_name_new, '^\(.*\.\)\?\zs.*\ze' )
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000729
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
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000758 if g:loaded_dbext > 300
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000759 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 )
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000776 let table_cols = split(table_cols_str, '\n')
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000777 endif
778
779 endif
780
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000781 if g:loaded_dbext > 300
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000782 exec 'DBSetOption use_tbl_alias='.saveSettingAlias
783 endif
784
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000785 " 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.
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000788 if a:list_type == 'csv' && !empty(table_cols)
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000789 let cols = join(table_cols, ', ')
790 let cols = s:SQLCAddAlias(table_name, table_alias, cols)
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000791 let table_cols = [cols]
792 endif
793
794 return table_cols
795endfunction