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