blob: aae92e61469b556f456702f0fe21fd360245af91 [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>
4" Version: 7.0
5" Last Change: 2009 Jan 04
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
12" Version 7.0
13" Better handling of object names
14" Version 6.0
15" Supports object names with spaces "my table name"
16"
Bram Moolenaare2f98b92006-03-29 21:18:24 +000017" Set completion with CTRL-X CTRL-O to autoloaded function.
18" This check is in place in case this script is
19" sourced directly instead of using the autoload feature.
20if exists('&omnifunc')
21 " Do not set the option if already set since this
22 " results in an E117 warning.
23 if &omnifunc == ""
24 setlocal omnifunc=sqlcomplete#Complete
25 endif
26endif
27
28if exists('g:loaded_sql_completion')
29 finish
30endif
Bram Moolenaar5c736222010-01-06 20:54:52 +010031let g:loaded_sql_completion = 70
Bram Moolenaare2f98b92006-03-29 21:18:24 +000032
33" Maintains filename of dictionary
Bram Moolenaar910f66f2006-04-05 20:41:53 +000034let s:sql_file_table = ""
35let s:sql_file_procedure = ""
36let s:sql_file_view = ""
Bram Moolenaare2f98b92006-03-29 21:18:24 +000037
38" Define various arrays to be used for caching
Bram Moolenaar910f66f2006-04-05 20:41:53 +000039let s:tbl_name = []
40let s:tbl_alias = []
41let s:tbl_cols = []
42let s:syn_list = []
43let s:syn_value = []
Bram Moolenaare2f98b92006-03-29 21:18:24 +000044
45" Used in conjunction with the syntaxcomplete plugin
Bram Moolenaar910f66f2006-04-05 20:41:53 +000046let s:save_inc = ""
47let s:save_exc = ""
Bram Moolenaare2f98b92006-03-29 21:18:24 +000048if exists('g:omni_syntax_group_include_sql')
49 let s:save_inc = g:omni_syntax_group_include_sql
50endif
51if exists('g:omni_syntax_group_exclude_sql')
52 let s:save_exc = g:omni_syntax_group_exclude_sql
53endif
54
55" Used with the column list
Bram Moolenaar910f66f2006-04-05 20:41:53 +000056let s:save_prev_table = ""
Bram Moolenaare2f98b92006-03-29 21:18:24 +000057
58" Default the option to verify table alias
59if !exists('g:omni_sql_use_tbl_alias')
60 let g:omni_sql_use_tbl_alias = 'a'
61endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +000062" Default syntax items to precache
63if !exists('g:omni_sql_precache_syntax_groups')
64 let g:omni_sql_precache_syntax_groups = [
65 \ 'syntax',
66 \ 'sqlKeyword',
67 \ 'sqlFunction',
68 \ 'sqlOption',
69 \ 'sqlType',
70 \ 'sqlStatement'
71 \ ]
72endif
Bram Moolenaareb3593b2006-04-22 22:33:57 +000073" Set ignorecase to the ftplugin standard
74if !exists('g:omni_sql_ignorecase')
75 let g:omni_sql_ignorecase = &ignorecase
76endif
77" During table completion, should the table list also
78" include the owner name
79if !exists('g:omni_sql_include_owner')
80 let g:omni_sql_include_owner = 0
81 if exists('g:loaded_dbext')
82 if g:loaded_dbext >= 300
83 " New to dbext 3.00, by default the table lists include the owner
84 " name of the table. This is used when determining how much of
85 " whatever has been typed should be replaced as part of the
86 " code replacement.
87 let g:omni_sql_include_owner = 1
88 endif
89 endif
90endif
Bram Moolenaare2f98b92006-03-29 21:18:24 +000091
92" This function is used for the 'omnifunc' option.
93function! sqlcomplete#Complete(findstart, base)
94
95 " Default to table name completion
96 let compl_type = 'table'
97 " Allow maps to specify what type of object completion they want
98 if exists('b:sql_compl_type')
99 let compl_type = b:sql_compl_type
100 endif
101
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000102 " First pass through this function determines how much of the line should
103 " be replaced by whatever is chosen from the completion list
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000104 if a:findstart
105 " Locate the start of the item, including "."
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000106 let line = getline('.')
107 let start = col('.') - 1
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000108 let lastword = -1
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000109 let begindot = 0
110 " Check if the first character is a ".", for column completion
111 if line[start - 1] == '.'
112 let begindot = 1
113 endif
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000114 while start > 0
Bram Moolenaar5c736222010-01-06 20:54:52 +0100115 " Additional code was required to handle objects which
116 " can contain spaces like "my table name".
117 if line[start - 1] !~ '\(\w\|\.\)'
118 " If the previous character is not a period or word character
119 break
120 " elseif line[start - 1] =~ '\(\w\|\s\+\)'
121 " let start -= 1
122 elseif line[start - 1] =~ '\w'
123 " If the previous character is word character continue back
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000124 let start -= 1
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000125 elseif line[start - 1] =~ '\.' &&
126 \ compl_type =~ 'column\|table\|view\|procedure'
Bram Moolenaar5c736222010-01-06 20:54:52 +0100127 " If the previous character is a period and we are completing
128 " an object which can be specified with a period like this:
129 " table_name.column_name
130 " owner_name.table_name
131
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000132 " If lastword has already been set for column completion
133 " break from the loop, since we do not also want to pickup
134 " a table name if it was also supplied.
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000135 if lastword != -1 && compl_type == 'column'
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000136 break
137 endif
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000138 " If column completion was specified stop at the "." if
139 " a . was specified, otherwise, replace all the way up
140 " to the owner name (if included).
141 if lastword == -1 && compl_type == 'column' && begindot == 1
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000142 let lastword = start
143 endif
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000144 " If omni_sql_include_owner = 0, do not include the table
145 " name as part of the substitution, so break here
146 if lastword == -1 &&
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000147 \ compl_type =~ 'table\|view\|procedure\column_csv' &&
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000148 \ g:omni_sql_include_owner == 0
149 let lastword = start
150 break
151 endif
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000152 let start -= 1
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000153 else
154 break
155 endif
156 endwhile
157
158 " Return the column of the last word, which is going to be changed.
159 " Remember the text that comes before it in s:prepended.
160 if lastword == -1
161 let s:prepended = ''
162 return start
163 endif
164 let s:prepended = strpart(line, start, lastword - start)
165 return lastword
166 endif
167
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000168 " Second pass through this function will determine what data to put inside
169 " of the completion list
170 " s:prepended is set by the first pass
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000171 let base = s:prepended . a:base
172
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000173 " Default the completion list to an empty list
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000174 let compl_list = []
175
176 " Default to table name completion
177 let compl_type = 'table'
178 " Allow maps to specify what type of object completion they want
179 if exists('b:sql_compl_type')
180 let compl_type = b:sql_compl_type
181 unlet b:sql_compl_type
182 endif
183
184 if compl_type == 'tableReset'
185 let compl_type = 'table'
186 let base = ''
187 endif
188
189 if compl_type == 'table' ||
190 \ compl_type == 'procedure' ||
191 \ compl_type == 'view'
192
193 " This type of completion relies upon the dbext.vim plugin
194 if s:SQLCCheck4dbext() == -1
195 return []
196 endif
197
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000198 " Allow the user to override the dbext plugin to specify whether
199 " the owner/creator should be included in the list
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000200 if g:loaded_dbext >= 300
201 let saveSetting = DB_listOption('dict_show_owner')
202 exec 'DBSetOption dict_show_owner='.(g:omni_sql_include_owner==1?'1':'0')
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000203 endif
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000204
205 let compl_type_uc = substitute(compl_type, '\w\+', '\u&', '')
Bram Moolenaar5c736222010-01-06 20:54:52 +0100206 " Same call below, no need to do it twice
207 " if s:sql_file_{compl_type} == ""
208 " let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type_uc)
209 " endif
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000210 let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type_uc)
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000211 if s:sql_file_{compl_type} != ""
212 if filereadable(s:sql_file_{compl_type})
213 let compl_list = readfile(s:sql_file_{compl_type})
214 endif
215 endif
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000216
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000217 if g:loaded_dbext > 300
218 exec 'DBSetOption dict_show_owner='.saveSetting
219 endif
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000220 elseif compl_type =~? 'column'
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000221
222 " This type of completion relies upon the dbext.vim plugin
223 if s:SQLCCheck4dbext() == -1
224 return []
225 endif
226
227 if base == ""
228 " The last time we displayed a column list we stored
229 " the table name. If the user selects a column list
230 " without a table name of alias present, assume they want
231 " the previous column list displayed.
232 let base = s:save_prev_table
233 endif
234
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000235 let owner = ''
236 let column = ''
237
238 if base =~ '\.'
239 " Check if the owner/creator has been specified
240 let owner = matchstr( base, '^\zs.*\ze\..*\..*' )
241 let table = matchstr( base, '^\(.*\.\)\?\zs.*\ze\..*' )
242 let column = matchstr( base, '.*\.\zs.*' )
243
244 " It is pretty well impossible to determine if the user
245 " has entered:
246 " owner.table
247 " table.column_prefix
248 " So there are a couple of things we can do to mitigate
249 " this issue.
250 " 1. Check if the dbext plugin has the option turned
251 " on to even allow owners
252 " 2. Based on 1, if the user is showing a table list
253 " and the DrillIntoTable (using <C-Right>) then
254 " this will be owner.table. In this case, we can
255 " check to see the table.column exists in the
256 " cached table list. If it does, then we have
257 " determined the user has actually chosen
258 " owner.table, not table.column_prefix.
259 let found = -1
260 if g:omni_sql_include_owner == 1 && owner == ''
261 if filereadable(s:sql_file_table)
262 let tbl_list = readfile(s:sql_file_table)
263 let found = index( tbl_list, ((table != '')?(table.'.'):'').column)
264 endif
265 endif
266 " If the table.column was found in the table list, we can safely assume
267 " the owner was not provided and shift the items appropriately.
268 " OR
269 " If the user has indicated not to use table owners at all and
270 " the base ends in a '.' we know they are not providing a column
271 " name, so we can shift the items appropriately.
272 if found != -1 || (g:omni_sql_include_owner == 0 && base !~ '\.$')
273 let owner = table
274 let table = column
275 let column = ''
276 endif
277 else
278 let table = base
279 endif
280
281 " Get anything after the . and consider this the table name
282 " If an owner has been specified, then we must consider the
283 " base to be a partial column name
284 " let base = matchstr( base, '^\(.*\.\)\?\zs.*' )
285
286 if table != ""
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000287 let s:save_prev_table = base
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000288 let list_type = ''
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000289
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000290 if compl_type == 'column_csv'
291 " Return one array element, with a comma separated
292 " list of values instead of multiple array entries
293 " for each column in the table.
294 let list_type = 'csv'
295 endif
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000296
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000297 let compl_list = s:SQLCGetColumns(table, list_type)
298 if column != ''
299 " If no column prefix has been provided and the table
300 " name was provided, append it to each of the items
301 " returned.
302 let compl_list = map(compl_list, "table.'.'.v:val")
303 if owner != ''
304 " If an owner has been provided append it to each of the
305 " items returned.
306 let compl_list = map(compl_list, "owner.'.'.v:val")
307 endif
308 else
309 let base = ''
310 endif
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000311
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000312 if compl_type == 'column_csv'
313 " Join the column array into 1 single element array
314 " but make the columns column separated
315 let compl_list = [join(compl_list, ', ')]
316 endif
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000317 endif
318 elseif compl_type == 'resetCache'
319 " Reset all cached items
320 let s:tbl_name = []
321 let s:tbl_alias = []
322 let s:tbl_cols = []
323 let s:syn_list = []
324 let s:syn_value = []
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000325
326 let msg = "All SQL cached items have been removed."
327 call s:SQLCWarningMsg(msg)
328 " Leave time for the user to read the error message
329 :sleep 2
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000330 else
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000331 let compl_list = s:SQLCGetSyntaxList(compl_type)
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000332 endif
333
334 if base != ''
Bram Moolenaar5c736222010-01-06 20:54:52 +0100335 " Filter the list based on the first few characters the user entered.
336 " Check if the text matches at the beginning
337 " or
338 " Match to a owner.table or alias.column type match
339 " or
340 " Handle names with spaces "my table name"
341 let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|^\\(\\w\\+\\.\\)\\?'.base.'\\)"'
342 " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\)"'
343 " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|\\(\\.\\)\\?'.base.'\\)"'
344 " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|\\([^.]*\\)\\?'.base.'\\)"'
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000345 let compl_list = filter(deepcopy(compl_list), expr)
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000346 endif
347
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000348 if exists('b:sql_compl_savefunc') && b:sql_compl_savefunc != ""
349 let &omnifunc = b:sql_compl_savefunc
350 endif
351
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000352 return compl_list
353endfunc
354
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000355function! sqlcomplete#PreCacheSyntax(...)
356 let syn_group_arr = []
357 if a:0 > 0
358 let syn_group_arr = a:1
359 else
360 let syn_group_arr = g:omni_sql_precache_syntax_groups
361 endif
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000362 " For each group specified in the list, precache all
363 " the sytnax items.
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000364 if !empty(syn_group_arr)
365 for group_name in syn_group_arr
366 call s:SQLCGetSyntaxList(group_name)
367 endfor
368 endif
369endfunction
370
371function! sqlcomplete#Map(type)
372 " Tell the SQL plugin what you want to complete
373 let b:sql_compl_type=a:type
374 " Record previous omnifunc, if the SQL completion
375 " is being used in conjunction with other filetype
376 " completion plugins
377 if &omnifunc != "" && &omnifunc != 'sqlcomplete#Complete'
378 " Record the previous omnifunc, the plugin
379 " will automatically set this back so that it
380 " does not interfere with other ftplugins settings
381 let b:sql_compl_savefunc=&omnifunc
382 endif
383 " Set the OMNI func for the SQL completion plugin
384 let &omnifunc='sqlcomplete#Complete'
385endfunction
386
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000387function! sqlcomplete#DrillIntoTable()
388 " If the omni popup window is visible
389 if pumvisible()
390 call sqlcomplete#Map('column')
391 " C-Y, makes the currently highlighted entry active
392 " and trigger the omni popup to be redisplayed
393 call feedkeys("\<C-Y>\<C-X>\<C-O>")
394 else
395 if has('win32')
396 " If the popup is not visible, simple perform the normal
397 " <C-Right> behaviour
398 exec "normal! \<C-Right>"
399 endif
400 endif
401 return ""
402endfunction
403
404function! sqlcomplete#DrillOutOfColumns()
405 " If the omni popup window is visible
406 if pumvisible()
407 call sqlcomplete#Map('tableReset')
408 " Trigger the omni popup to be redisplayed
409 call feedkeys("\<C-X>\<C-O>")
410 else
411 if has('win32')
412 " If the popup is not visible, simple perform the normal
413 " <C-Left> behaviour
414 exec "normal! \<C-Left>"
415 endif
416 endif
417 return ""
418endfunction
419
420function! s:SQLCWarningMsg(msg)
421 echohl WarningMsg
422 echomsg a:msg
423 echohl None
424endfunction
425
426function! s:SQLCErrorMsg(msg)
427 echohl ErrorMsg
428 echomsg a:msg
429 echohl None
430endfunction
431
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000432function! s:SQLCGetSyntaxList(syn_group)
433 let syn_group = a:syn_group
434 let compl_list = []
435
436 " Check if we have already cached the syntax list
437 let list_idx = index(s:syn_list, syn_group, 0, &ignorecase)
438 if list_idx > -1
439 " Return previously cached value
440 let compl_list = s:syn_value[list_idx]
441 else
442 " Request the syntax list items from the
443 " syntax completion plugin
444 if syn_group == 'syntax'
445 " Handle this special case. This allows the user
446 " to indicate they want all the syntax items available,
447 " so do not specify a specific include list.
448 let g:omni_syntax_group_include_sql = ''
449 else
450 " The user has specified a specific syntax group
451 let g:omni_syntax_group_include_sql = syn_group
452 endif
453 let g:omni_syntax_group_exclude_sql = ''
454 let syn_value = OmniSyntaxList()
455 let g:omni_syntax_group_include_sql = s:save_inc
456 let g:omni_syntax_group_exclude_sql = s:save_exc
457 " Cache these values for later use
458 let s:syn_list = add( s:syn_list, syn_group )
459 let s:syn_value = add( s:syn_value, syn_value )
460 let compl_list = syn_value
461 endif
462
463 return compl_list
464endfunction
465
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000466function! s:SQLCCheck4dbext()
467 if !exists('g:loaded_dbext')
468 let msg = "The dbext plugin must be loaded for dynamic SQL completion"
469 call s:SQLCErrorMsg(msg)
470 " Leave time for the user to read the error message
471 :sleep 2
472 return -1
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000473 elseif g:loaded_dbext < 600
474 let msg = "The dbext plugin must be at least version 5.30 " .
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000475 \ " for dynamic SQL completion"
476 call s:SQLCErrorMsg(msg)
477 " Leave time for the user to read the error message
478 :sleep 2
479 return -1
480 endif
481 return 1
482endfunction
483
484function! s:SQLCAddAlias(table_name, table_alias, cols)
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000485 " Strip off the owner if included
486 let table_name = matchstr(a:table_name, '\%(.\{-}\.\)\?\zs\(.*\)' )
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000487 let table_alias = a:table_alias
488 let cols = a:cols
489
490 if g:omni_sql_use_tbl_alias != 'n'
491 if table_alias == ''
492 if 'da' =~? g:omni_sql_use_tbl_alias
493 if table_name =~ '_'
494 " Treat _ as separators since people often use these
495 " for word separators
496 let save_keyword = &iskeyword
497 setlocal iskeyword-=_
498
499 " Get the first letter of each word
500 " [[:alpha:]] is used instead of \w
501 " to catch extended accented characters
502 "
503 let table_alias = substitute(
504 \ table_name,
505 \ '\<[[:alpha:]]\+\>_\?',
506 \ '\=strpart(submatch(0), 0, 1)',
507 \ 'g'
508 \ )
509 " Restore original value
510 let &iskeyword = save_keyword
511 elseif table_name =~ '\u\U'
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000512 let table_alias = substitute(
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000513 \ table_name, '\(\u\)\U*', '\1', 'g')
514 else
515 let table_alias = strpart(table_name, 0, 1)
516 endif
517 endif
518 endif
519 if table_alias != ''
520 " Following a word character, make sure there is a . and no spaces
521 let table_alias = substitute(table_alias, '\w\zs\.\?\s*$', '.', '')
522 if 'a' =~? g:omni_sql_use_tbl_alias && a:table_alias == ''
523 let table_alias = inputdialog("Enter table alias:", table_alias)
524 endif
525 endif
526 if table_alias != ''
527 let cols = substitute(cols, '\<\w', table_alias.'&', 'g')
528 endif
529 endif
530
531 return cols
532endfunction
533
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000534function! s:SQLCGetObjectOwner(object)
535 " The owner regex matches a word at the start of the string which is
536 " followed by a dot, but doesn't include the dot in the result.
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000537 " ^ - from beginning of line
538 " \("\|\[\)\? - ignore any quotes
539 " \zs - start the match now
540 " .\{-} - get owner name
541 " \ze - end the match
542 " \("\|\[\)\? - ignore any quotes
543 " \. - must by followed by a .
544 " let owner = matchstr( a:object, '^\s*\zs.*\ze\.' )
545 let owner = matchstr( a:object, '^\("\|\[\)\?\zs\.\{-}\ze\("\|\]\)\?\.' )
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000546 return owner
547endfunction
548
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000549function! s:SQLCGetColumns(table_name, list_type)
Bram Moolenaar83e138c2007-05-05 18:27:07 +0000550 " Check if the table name was provided as part of the column name
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000551 let table_name = matchstr(a:table_name, '^["\[\]a-zA-Z0-9_ ]\+\ze\.\?')
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000552 let table_cols = []
553 let table_alias = ''
554 let move_to_top = 1
555
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000556 let table_name = substitute(table_name, '\s*\(.\{-}\)\s*$', '\1', 'g')
557
558 " If the table name was given as:
559 " where c.
560 let table_name = substitute(table_name, '^\c\(WHERE\|AND\|OR\)\s\+', '', '')
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000561 if g:loaded_dbext >= 300
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000562 let saveSettingAlias = DB_listOption('use_tbl_alias')
563 exec 'DBSetOption use_tbl_alias=n'
564 endif
565
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000566 let table_name_stripped = substitute(table_name, '["\[\]]*', '', 'g')
567
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000568 " Check if we have already cached the column list for this table
569 " by its name
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000570 let list_idx = index(s:tbl_name, table_name_stripped, 0, &ignorecase)
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000571 if list_idx > -1
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000572 let table_cols = split(s:tbl_cols[list_idx], '\n')
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000573 else
574 " Check if we have already cached the column list for this table
575 " by its alias, assuming the table_name provided was actually
576 " the alias for the table instead
577 " select *
578 " from area a
579 " where a.
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000580 let list_idx = index(s:tbl_alias, table_name_stripped, 0, &ignorecase)
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000581 if list_idx > -1
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000582 let table_alias = table_name_stripped
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000583 let table_name = s:tbl_name[list_idx]
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000584 let table_cols = split(s:tbl_cols[list_idx], '\n')
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000585 endif
586 endif
587
588 " If we have not found a cached copy of the table
589 " And the table ends in a "." or we are looking for a column list
590 " if list_idx == -1 && (a:table_name =~ '\.' || b:sql_compl_type =~ 'column')
591 " if list_idx == -1 && (a:table_name =~ '\.' || a:list_type =~ 'csv')
592 if list_idx == -1
593 let saveY = @y
594 let saveSearch = @/
595 let saveWScan = &wrapscan
596 let curline = line(".")
597 let curcol = col(".")
598
599 " Do not let searchs wrap
600 setlocal nowrapscan
601 " If . was entered, look at the word just before the .
602 " We are looking for something like this:
603 " select *
604 " from customer c
605 " where c.
606 " So when . is pressed, we need to find 'c'
607 "
608
609 " Search backwards to the beginning of the statement
610 " and do NOT wrap
611 " exec 'silent! normal! v?\<\(select\|update\|delete\|;\)\>'."\n".'"yy'
612 exec 'silent! normal! ?\<\(select\|update\|delete\|;\)\>'."\n"
613
614 " Start characterwise visual mode
615 " Advance right one character
616 " Search foward until one of the following:
617 " 1. Another select/update/delete statement
618 " 2. A ; at the end of a line (the delimiter)
619 " 3. The end of the file (incase no delimiter)
620 " Yank the visually selected text into the "y register.
621 exec 'silent! normal! vl/\(\<select\>\|\<update\>\|\<delete\>\|;\s*$\|\%$\)'."\n".'"yy'
622
623 let query = @y
624 let query = substitute(query, "\n", ' ', 'g')
625 let found = 0
626
627 " if query =~? '^\(select\|update\|delete\)'
628 if query =~? '^\(select\)'
629 let found = 1
630 " \(\(\<\w\+\>\)\.\)\? -
631 " 'from.\{-}' - Starting at the from clause
632 " '\zs\(\(\<\w\+\>\)\.\)\?' - Get the owner name (optional)
633 " '\<\w\+\>\ze' - Get the table name
634 " '\s\+\<'.table_name.'\>' - Followed by the alias
635 " '\s*\.\@!.*' - Cannot be followed by a .
636 " '\(\<where\>\|$\)' - Must be followed by a WHERE clause
637 " '.*' - Exclude the rest of the line in the match
638 let table_name_new = matchstr(@y,
639 \ 'from.\{-}'.
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000640 \ '\zs\(\("\|\[\)\?.\{-}\("\|\]\)\.\)\?'.
641 \ '\("\|\[\)\?.\{-}\("\|\]\)\ze'.
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000642 \ '\s\+\%(as\s\+\)\?\<'.
643 \ matchstr(table_name, '.\{-}\ze\.\?$').
644 \ '\>'.
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000645 \ '\s*\.\@!.*'.
646 \ '\(\<where\>\|$\)'.
647 \ '.*'
648 \ )
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000649
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000650 if table_name_new != ''
651 let table_alias = table_name
652 let table_name = table_name_new
653
654 let list_idx = index(s:tbl_name, table_name, 0, &ignorecase)
655 if list_idx > -1
656 let table_cols = split(s:tbl_cols[list_idx])
657 let s:tbl_name[list_idx] = table_name
658 let s:tbl_alias[list_idx] = table_alias
659 else
660 let list_idx = index(s:tbl_alias, table_name, 0, &ignorecase)
661 if list_idx > -1
662 let table_cols = split(s:tbl_cols[list_idx])
663 let s:tbl_name[list_idx] = table_name
664 let s:tbl_alias[list_idx] = table_alias
665 endif
666 endif
667
668 endif
669 else
670 " Simply assume it is a table name provided with a . on the end
671 let found = 1
672 endif
673
674 let @y = saveY
675 let @/ = saveSearch
676 let &wrapscan = saveWScan
677
678 " Return to previous location
679 call cursor(curline, curcol)
680
681 if found == 0
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000682 if g:loaded_dbext > 300
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000683 exec 'DBSetOption use_tbl_alias='.saveSettingAlias
684 endif
685
686 " Not a SQL statement, do not display a list
687 return []
688 endif
689 endif
690
691 if empty(table_cols)
692 " Specify silent mode, no messages to the user (tbl, 1)
693 " Specify do not comma separate (tbl, 1, 1)
694 let table_cols_str = DB_getListColumn(table_name, 1, 1)
695
696 if table_cols_str != ""
697 let s:tbl_name = add( s:tbl_name, table_name )
698 let s:tbl_alias = add( s:tbl_alias, table_alias )
699 let s:tbl_cols = add( s:tbl_cols, table_cols_str )
Bram Moolenaar8c8de832008-06-24 22:58:06 +0000700 let table_cols = split(table_cols_str, '\n')
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000701 endif
702
703 endif
704
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000705 if g:loaded_dbext > 300
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000706 exec 'DBSetOption use_tbl_alias='.saveSettingAlias
707 endif
708
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000709 " If the user has asked for a comma separate list of column
710 " values, ask the user if they want to prepend each column
711 " with a tablename alias.
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000712 if a:list_type == 'csv' && !empty(table_cols)
Bram Moolenaarf193fff2006-04-27 00:02:13 +0000713 let cols = join(table_cols, ', ')
714 let cols = s:SQLCAddAlias(table_name, table_alias, cols)
Bram Moolenaare2f98b92006-03-29 21:18:24 +0000715 let table_cols = [cols]
716 endif
717
718 return table_cols
719endfunction
720