1" Vim OMNI completion script for SQL 2" Language: SQL 3" Maintainer: David Fishburn <dfishburn dot vim at gmail dot com> 4" Version: 10.0 5" Last Change: 2010 Jun 11 6" Usage: For detailed help 7" ":help sql.txt" 8" or ":help ft-sql-omni" 9" or read $VIMRUNTIME/doc/sql.txt 10 11" History 12" 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" 28" 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. 34" 35" 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. 38" 39" Version 7.0 40" Better handling of object names 41" 42" Version 6.0 43" Supports object names with spaces "my table name" 44" 45" 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 59let g:loaded_sql_completion = 100 60 61" Maintains filename of dictionary 62let s:sql_file_table = "" 63let s:sql_file_procedure = "" 64let s:sql_file_view = "" 65 66" Define various arrays to be used for caching 67let s:tbl_name = [] 68let s:tbl_alias = [] 69let s:tbl_cols = [] 70let s:syn_list = [] 71let s:syn_value = [] 72 73" Used in conjunction with the syntaxcomplete plugin 74let s:save_inc = "" 75let s:save_exc = "" 76if 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 84let s:save_prev_table = "" 85 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 90" 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 101" 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 119 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 130 " First pass through this function determines how much of the line should 131 " be replaced by whatever is chosen from the completion list 132 if a:findstart 133 " Locate the start of the item, including "." 134 let line = getline('.') 135 let start = col('.') - 1 136 let lastword = -1 137 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 142 while start > 0 143 " 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 152 let start -= 1 153 elseif line[start - 1] =~ '\.' && 154 \ compl_type =~ 'column\|table\|view\|procedure' 155 " 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 160 " 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. 163 if lastword != -1 && compl_type == 'column' 164 break 165 endif 166 " 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 170 let lastword = start 171 endif 172 " 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 && 175 \ compl_type =~ 'table\|view\|procedure\column_csv' && 176 \ g:omni_sql_include_owner == 0 177 let lastword = start 178 break 179 endif 180 let start -= 1 181 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 196 " 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 199 let base = s:prepended . a:base 200 201 " Default the completion list to an empty list 202 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 226 " Allow the user to override the dbext plugin to specify whether 227 " the owner/creator should be included in the list 228 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') 231 endif 232 233 let compl_type_uc = substitute(compl_type, '\w\+', '\u&', '') 234 " 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 238 let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type_uc) 239 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 244 245 if g:loaded_dbext > 300 246 exec 'DBSetOption dict_show_owner='.saveSetting 247 endif 248 elseif compl_type =~? 'column' 249 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 263 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 281 " and the DrillIntoTable (using <Right>) then 282 " 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 != "" 315 let s:save_prev_table = base 316 let list_type = '' 317 318 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 324 325 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 339 340 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 345 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 = [] 353 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 358 else 359 let compl_list = s:SQLCGetSyntaxList(compl_type) 360 endif 361 362 if base != '' 363 " 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.'\\)"' 373 let compl_list = filter(deepcopy(compl_list), expr) 374 endif 375 376 if exists('b:sql_compl_savefunc') && b:sql_compl_savefunc != "" 377 let &omnifunc = b:sql_compl_savefunc 378 endif 379 380 return compl_list 381endfunc 382 383function! sqlcomplete#PreCacheSyntax(...) 384 let syn_group_arr = [] 385 let syn_items = [] 386 387 if a:0 > 0 388 if type(a:1) != 3 389 call s:SQLCWarningMsg("Parameter is not a list. Example:['syntaxGroup1', 'syntaxGroup2']") 390 return '' 391 endif 392 let syn_group_arr = a:1 393 else 394 let syn_group_arr = g:omni_sql_precache_syntax_groups 395 endif 396 " For each group specified in the list, precache all 397 " the sytnax items. 398 if !empty(syn_group_arr) 399 for group_name in syn_group_arr 400 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 430 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 450function! 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 456 call feedkeys("\<C-Y>\<C-X>\<C-O>", 'n') 457 else 458 " 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")' 464 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 475 " 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")' 481 endif 482 return "" 483endfunction 484 485function! s:SQLCWarningMsg(msg) 486 echohl WarningMsg 487 echomsg 'SQLComplete:'.a:msg 488 echohl None 489endfunction 490 491function! s:SQLCErrorMsg(msg) 492 echohl ErrorMsg 493 echomsg 'SQLComplete:'.a:msg 494 echohl None 495endfunction 496 497function! 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 = '' 519 let syn_value = syntaxcomplete#OmniSyntaxList() 520 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 531function! 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 538 elseif g:loaded_dbext < 600 539 let msg = "The dbext plugin must be at least version 5.30 " . 540 \ " 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) 550 " Strip off the owner if included 551 let table_name = matchstr(a:table_name, '\%(.\{-}\.\)\?\zs\(.*\)' ) 552 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' 577 let table_alias = substitute( 578 \ 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 599function! 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. 602 " ^ - 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\("\|\]\)\?\.' ) 611 return owner 612endfunction 613 614function! s:SQLCGetColumns(table_name, list_type) 615 " Check if the table name was provided as part of the column name 616 let table_name = matchstr(a:table_name, '^["\[\]a-zA-Z0-9_ ]\+\ze\.\?') 617 let table_cols = [] 618 let table_alias = '' 619 let move_to_top = 1 620 621 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\+', '', '') 626 if g:loaded_dbext >= 300 627 let saveSettingAlias = DB_listOption('use_tbl_alias') 628 exec 'DBSetOption use_tbl_alias=n' 629 endif 630 631 let table_name_stripped = substitute(table_name, '["\[\]]*', '', 'g') 632 633 " Check if we have already cached the column list for this table 634 " by its name 635 let list_idx = index(s:tbl_name, table_name_stripped, 0, &ignorecase) 636 if list_idx > -1 637 let table_cols = split(s:tbl_cols[list_idx], '\n') 638 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. 645 let list_idx = index(s:tbl_alias, table_name_stripped, 0, &ignorecase) 646 if list_idx > -1 647 let table_alias = table_name_stripped 648 let table_name = s:tbl_name[list_idx] 649 let table_cols = split(s:tbl_cols[list_idx], '\n') 650 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' 677 exec 'silent! normal! ?\<\c\(select\|update\|delete\|;\)\>'."\n" 678 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. 686 exec 'silent! normal! vl/\c\(\<select\>\|\<update\>\|\<delete\>\|;\s*$\|\%$\)'."\n".'"yy' 687 688 let query = @y 689 let query = substitute(query, "\n", ' ', 'g') 690 let found = 0 691 692 " if query =~? '^\c\(select\)' 693 if query =~? '^\(select\|update\|delete\)' 694 let found = 1 695 " \(\(\<\w\+\>\)\.\)\? - 696 " '\c\(from\|join\|,\).\{-}' - Starting at the from clause (case insensitive) 697 " '\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 703 " 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 " \ ) 714 let table_name_new = matchstr(@y, 715 \ '\c\(\<from\>\|\<join\>\|,\)\s*'. 716 \ '\zs\(\("\|\[\)\?\w\+\("\|\]\)\?\.\)\?'. 717 \ '\("\|\[\)\?\w\+\("\|\]\)\?\ze'. 718 \ '\s\+\%(as\s\+\)\?\<'. 719 \ matchstr(table_name, '.\{-}\ze\.\?$'). 720 \ '\>'. 721 \ '\s*\.\@!.*'. 722 \ '\(\<where\>\|$\)'. 723 \ '.*' 724 \ ) 725 726 if table_name_new != '' 727 let table_alias = table_name 728 let table_name = matchstr( table_name_new, '^\(.*\.\)\?\zs.*\ze' ) 729 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 758 if g:loaded_dbext > 300 759 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 ) 776 let table_cols = split(table_cols_str, '\n') 777 endif 778 779 endif 780 781 if g:loaded_dbext > 300 782 exec 'DBSetOption use_tbl_alias='.saveSettingAlias 783 endif 784 785 " 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. 788 if a:list_type == 'csv' && !empty(table_cols) 789 let cols = join(table_cols, ', ') 790 let cols = s:SQLCAddAlias(table_name, table_alias, cols) 791 let table_cols = [cols] 792 endif 793 794 return table_cols 795endfunction 796