1" Vim OMNI completion script for SQL 2" Language: SQL 3" Maintainer: David Fishburn <dfishburn dot vim at gmail dot com> 4" Version: 12.0 5" Last Change: 2012 Feb 08 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 12.0 13" - Partial column name completion did not work when a table 14" name or table alias was provided (Jonas Enberg). 15" - Improved the handling of column completion. First we match any 16" columns from a previous completion. If not matches are found, we 17" consider the partial name to be a table or table alias for the 18" query and attempt to match on it. 19" 20" Version 11.0 21" Added g:omni_sql_default_compl_type variable 22" - You can specify which type of completion to default to 23" when pressing <C-X><C-O>. The entire list of available 24" choices can be found in the calls to sqlcomplete#Map in: 25" ftplugin/sql.vim 26" 27" Version 10.0 28" Updated PreCacheSyntax() 29" - Now returns a List of the syntax items it finds. 30" This allows other plugins / scripts to use this list for their own 31" purposes. In this case XPTemplate can use them for a Choose list. 32" - Verifies the parameters are the correct type and displays a 33" warning if not. 34" - Verifies the parameters are the correct type and displays a 35" warning if not. 36" Updated SQLCWarningMsg() 37" - Prepends warning message with SQLComplete so you know who issued 38" the warning. 39" Updated SQLCErrorMsg() 40" - Prepends error message with SQLComplete so you know who issued 41" the error. 42" 43" Version 9.0 44" This change removes some of the support for tables with spaces in their 45" names in order to simplify the regexes used to pull out query table 46" aliases for more robust table name and column name code completion. 47" Full support for "table names with spaces" can be added in again 48" after 7.3. 49" 50" Version 8.0 51" Incorrectly re-executed the g:ftplugin_sql_omni_key_right and g:ftplugin_sql_omni_key_left 52" when drilling in and out of a column list for a table. 53" 54" Version 7.0 55" Better handling of object names 56" 57" Version 6.0 58" Supports object names with spaces "my table name" 59" 60" Set completion with CTRL-X CTRL-O to autoloaded function. 61" This check is in place in case this script is 62" sourced directly instead of using the autoload feature. 63if exists('&omnifunc') 64 " Do not set the option if already set since this 65 " results in an E117 warning. 66 if &omnifunc == "" 67 setlocal omnifunc=sqlcomplete#Complete 68 endif 69endif 70 71if exists('g:loaded_sql_completion') 72 finish 73endif 74let g:loaded_sql_completion = 120 75 76" Maintains filename of dictionary 77let s:sql_file_table = "" 78let s:sql_file_procedure = "" 79let s:sql_file_view = "" 80 81" Define various arrays to be used for caching 82let s:tbl_name = [] 83let s:tbl_alias = [] 84let s:tbl_cols = [] 85let s:syn_list = [] 86let s:syn_value = [] 87 88" Used in conjunction with the syntaxcomplete plugin 89let s:save_inc = "" 90let s:save_exc = "" 91if exists('g:omni_syntax_group_include_sql') 92 let s:save_inc = g:omni_syntax_group_include_sql 93endif 94if exists('g:omni_syntax_group_exclude_sql') 95 let s:save_exc = g:omni_syntax_group_exclude_sql 96endif 97 98" Used with the column list 99let s:save_prev_table = "" 100 101" Default the option to verify table alias 102if !exists('g:omni_sql_use_tbl_alias') 103 let g:omni_sql_use_tbl_alias = 'a' 104endif 105" Default syntax items to precache 106if !exists('g:omni_sql_precache_syntax_groups') 107 let g:omni_sql_precache_syntax_groups = [ 108 \ 'syntax', 109 \ 'sqlKeyword', 110 \ 'sqlFunction', 111 \ 'sqlOption', 112 \ 'sqlType', 113 \ 'sqlStatement' 114 \ ] 115endif 116" Set ignorecase to the ftplugin standard 117if !exists('g:omni_sql_ignorecase') 118 let g:omni_sql_ignorecase = &ignorecase 119endif 120" During table completion, should the table list also 121" include the owner name 122if !exists('g:omni_sql_include_owner') 123 let g:omni_sql_include_owner = 0 124 if exists('g:loaded_dbext') 125 if g:loaded_dbext >= 300 126 " New to dbext 3.00, by default the table lists include the owner 127 " name of the table. This is used when determining how much of 128 " whatever has been typed should be replaced as part of the 129 " code replacement. 130 let g:omni_sql_include_owner = 1 131 endif 132 endif 133endif 134" Default type of completion used when <C-X><C-O> is pressed 135if !exists('g:omni_sql_default_compl_type') 136 let g:omni_sql_default_compl_type = 'table' 137endif 138 139" This function is used for the 'omnifunc' option. 140function! sqlcomplete#Complete(findstart, base) 141 142 " Default to table name completion 143 let compl_type = 'table' 144 " Allow maps to specify what type of object completion they want 145 if exists('b:sql_compl_type') 146 let compl_type = b:sql_compl_type 147 endif 148 149 " First pass through this function determines how much of the line should 150 " be replaced by whatever is chosen from the completion list 151 if a:findstart 152 " Locate the start of the item, including "." 153 let line = getline('.') 154 let start = col('.') - 1 155 let lastword = -1 156 let begindot = 0 157 " Check if the first character is a ".", for column completion 158 if line[start - 1] == '.' 159 let begindot = 1 160 endif 161 while start > 0 162 " Additional code was required to handle objects which 163 " can contain spaces like "my table name". 164 if line[start - 1] !~ '\(\w\|\.\)' 165 " If the previous character is not a period or word character 166 break 167 " elseif line[start - 1] =~ '\(\w\|\s\+\)' 168 " let start -= 1 169 elseif line[start - 1] =~ '\w' 170 " If the previous character is word character continue back 171 let start -= 1 172 elseif line[start - 1] =~ '\.' && 173 \ compl_type =~ 'column\|table\|view\|procedure' 174 " If the previous character is a period and we are completing 175 " an object which can be specified with a period like this: 176 " table_name.column_name 177 " owner_name.table_name 178 179 " If lastword has already been set for column completion 180 " break from the loop, since we do not also want to pickup 181 " a table name if it was also supplied. 182 if lastword != -1 && compl_type == 'column' 183 break 184 endif 185 " If column completion was specified stop at the "." if 186 " a . was specified, otherwise, replace all the way up 187 " to the owner name (if included). 188 if lastword == -1 && compl_type == 'column' && begindot == 1 189 let lastword = start 190 endif 191 " If omni_sql_include_owner = 0, do not include the table 192 " name as part of the substitution, so break here 193 if lastword == -1 && 194 \ compl_type =~ 'table\|view\|procedure\column_csv' && 195 \ g:omni_sql_include_owner == 0 196 let lastword = start 197 break 198 endif 199 let start -= 1 200 else 201 break 202 endif 203 endwhile 204 205 " Return the column of the last word, which is going to be changed. 206 " Remember the text that comes before it in s:prepended. 207 if lastword == -1 208 let s:prepended = '' 209 return start 210 endif 211 let s:prepended = strpart(line, start, lastword - start) 212 return lastword 213 endif 214 215 " Second pass through this function will determine what data to put inside 216 " of the completion list 217 " s:prepended is set by the first pass 218 let base = s:prepended . a:base 219 220 " Default the completion list to an empty list 221 let compl_list = [] 222 223 " Default to table name completion 224 let compl_type = g:omni_sql_default_compl_type 225 " Allow maps to specify what type of object completion they want 226 if exists('b:sql_compl_type') 227 let compl_type = b:sql_compl_type 228 unlet b:sql_compl_type 229 endif 230 231 if compl_type == 'tableReset' 232 let compl_type = 'table' 233 let base = '' 234 endif 235 236 if compl_type == 'table' || 237 \ compl_type == 'procedure' || 238 \ compl_type == 'view' 239 240 " This type of completion relies upon the dbext.vim plugin 241 if s:SQLCCheck4dbext() == -1 242 return [] 243 endif 244 245 " Allow the user to override the dbext plugin to specify whether 246 " the owner/creator should be included in the list 247 if g:loaded_dbext >= 300 248 let saveSetting = DB_listOption('dict_show_owner') 249 exec 'DBSetOption dict_show_owner='.(g:omni_sql_include_owner==1?'1':'0') 250 endif 251 252 let compl_type_uc = substitute(compl_type, '\w\+', '\u&', '') 253 " Same call below, no need to do it twice 254 " if s:sql_file_{compl_type} == "" 255 " let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type_uc) 256 " endif 257 let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type_uc) 258 if s:sql_file_{compl_type} != "" 259 if filereadable(s:sql_file_{compl_type}) 260 let compl_list = readfile(s:sql_file_{compl_type}) 261 endif 262 endif 263 264 if g:loaded_dbext > 300 265 exec 'DBSetOption dict_show_owner='.saveSetting 266 endif 267 elseif compl_type =~? 'column' 268 269 " This type of completion relies upon the dbext.vim plugin 270 if s:SQLCCheck4dbext() == -1 271 return [] 272 endif 273 274 if base == "" 275 " The last time we displayed a column list we stored 276 " the table name. If the user selects a column list 277 " without a table name of alias present, assume they want 278 " the previous column list displayed. 279 let base = s:save_prev_table 280 endif 281 282 let owner = '' 283 let column = '' 284 285 if base =~ '\.' 286 " Check if the owner/creator has been specified 287 let owner = matchstr( base, '^\zs.*\ze\..*\..*' ) 288 let table = matchstr( base, '^\(.*\.\)\?\zs.*\ze\..*' ) 289 let column = matchstr( base, '.*\.\zs.*' ) 290 291 " It is pretty well impossible to determine if the user 292 " has entered: 293 " owner.table 294 " table.column_prefix 295 " So there are a couple of things we can do to mitigate 296 " this issue. 297 " 1. Check if the dbext plugin has the option turned 298 " on to even allow owners 299 " 2. Based on 1, if the user is showing a table list 300 " and the DrillIntoTable (using <Right>) then 301 " this will be owner.table. In this case, we can 302 " check to see the table.column exists in the 303 " cached table list. If it does, then we have 304 " determined the user has actually chosen 305 " owner.table, not table.column_prefix. 306 let found = -1 307 if g:omni_sql_include_owner == 1 && owner == '' 308 if filereadable(s:sql_file_table) 309 let tbl_list = readfile(s:sql_file_table) 310 let found = index( tbl_list, ((table != '')?(table.'.'):'').column) 311 endif 312 endif 313 " If the table.column was found in the table list, we can safely assume 314 " the owner was not provided and shift the items appropriately. 315 " OR 316 " If the user has indicated not to use table owners at all and 317 " the base ends in a '.' we know they are not providing a column 318 " name, so we can shift the items appropriately. 319 " if found != -1 || (g:omni_sql_include_owner == 0 && base !~ '\.$') 320 " let owner = table 321 " let table = column 322 " let column = '' 323 " endif 324 else 325 " If no "." was provided and the user asked for 326 " column level completion, first attempt the match 327 " on any previous column lists. If the user asked 328 " for a list of columns comma separated, continue as usual. 329 if compl_type == 'column' && s:save_prev_table != '' 330 " The last time we displayed a column list we stored 331 " the table name. If the user selects a column list 332 " without a table name of alias present, assume they want 333 " the previous column list displayed. 334 let table = s:save_prev_table 335 let list_type = '' 336 337 let compl_list = s:SQLCGetColumns(table, list_type) 338 if ! empty(compl_list) 339 " If no column prefix has been provided and the table 340 " name was provided, append it to each of the items 341 " returned. 342 let compl_list = filter(deepcopy(compl_list), 'v:val=~"^'.base.'"' ) 343 344 " If not empty, we have a match on columns 345 " return the list 346 if ! empty(compl_list) 347 return compl_list 348 endif 349 endif 350 endif 351 " Since no columns were found to match the base supplied 352 " assume the user is trying to complete the column list 353 " for a table (and or an alias to a table). 354 let table = base 355 endif 356 357 " Get anything after the . and consider this the table name 358 " If an owner has been specified, then we must consider the 359 " base to be a partial column name 360 " let base = matchstr( base, '^\(.*\.\)\?\zs.*' ) 361 362 if table != "" 363 let s:save_prev_table = base 364 let list_type = '' 365 366 if compl_type == 'column_csv' 367 " Return one array element, with a comma separated 368 " list of values instead of multiple array entries 369 " for each column in the table. 370 let list_type = 'csv' 371 endif 372 373 let compl_list = s:SQLCGetColumns(table, list_type) 374 if column != '' 375 " If no column prefix has been provided and the table 376 " name was provided, append it to each of the items 377 " returned. 378 let compl_list = map(compl_list, 'table.".".v:val') 379 if owner != '' 380 " If an owner has been provided append it to each of the 381 " items returned. 382 let compl_list = map(compl_list, 'owner.".".v:val') 383 endif 384 else 385 let base = '' 386 endif 387 388 if compl_type == 'column_csv' 389 " Join the column array into 1 single element array 390 " but make the columns column separated 391 let compl_list = [join(compl_list, ', ')] 392 endif 393 endif 394 elseif compl_type == 'resetCache' 395 " Reset all cached items 396 let s:tbl_name = [] 397 let s:tbl_alias = [] 398 let s:tbl_cols = [] 399 let s:syn_list = [] 400 let s:syn_value = [] 401 402 let msg = "All SQL cached items have been removed." 403 call s:SQLCWarningMsg(msg) 404 " Leave time for the user to read the error message 405 :sleep 2 406 else 407 let compl_list = s:SQLCGetSyntaxList(compl_type) 408 endif 409 410 if base != '' 411 " Filter the list based on the first few characters the user entered. 412 " Check if the text matches at the beginning 413 " \\(^.base.'\\) 414 " or 415 " Match to a owner.table or alias.column type match 416 " ^\\(\\w\\+\\.\\)\\?'.base.'\\) 417 " or 418 " Handle names with spaces "my table name" 419 " "\\(^'.base.'\\|^\\(\\w\\+\\.\\)\\?'.base.'\\)"' 420 " 421 let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|^\\(\\w\\+\\.\\)\\?'.base.'\\)"' 422 " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\)"' 423 " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|\\(\\.\\)\\?'.base.'\\)"' 424 " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|\\([^.]*\\)\\?'.base.'\\)"' 425 let compl_list = filter(deepcopy(compl_list), expr) 426 endif 427 428 if exists('b:sql_compl_savefunc') && b:sql_compl_savefunc != "" 429 let &omnifunc = b:sql_compl_savefunc 430 endif 431 432 return compl_list 433endfunc 434 435function! sqlcomplete#PreCacheSyntax(...) 436 let syn_group_arr = [] 437 let syn_items = [] 438 439 if a:0 > 0 440 if type(a:1) != 3 441 call s:SQLCWarningMsg("Parameter is not a list. Example:['syntaxGroup1', 'syntaxGroup2']") 442 return '' 443 endif 444 let syn_group_arr = a:1 445 else 446 let syn_group_arr = g:omni_sql_precache_syntax_groups 447 endif 448 " For each group specified in the list, precache all 449 " the sytnax items. 450 if !empty(syn_group_arr) 451 for group_name in syn_group_arr 452 let syn_items = extend( syn_items, s:SQLCGetSyntaxList(group_name) ) 453 endfor 454 endif 455 456 return syn_items 457endfunction 458 459function! sqlcomplete#ResetCacheSyntax(...) 460 let syn_group_arr = [] 461 462 if a:0 > 0 463 if type(a:1) != 3 464 call s:SQLCWarningMsg("Parameter is not a list. Example:['syntaxGroup1', 'syntaxGroup2']") 465 return '' 466 endif 467 let syn_group_arr = a:1 468 else 469 let syn_group_arr = g:omni_sql_precache_syntax_groups 470 endif 471 " For each group specified in the list, precache all 472 " the sytnax items. 473 if !empty(syn_group_arr) 474 for group_name in syn_group_arr 475 let list_idx = index(s:syn_list, group_name, 0, &ignorecase) 476 if list_idx > -1 477 " Remove from list of groups 478 call remove( s:syn_list, list_idx ) 479 " Remove from list of keywords 480 call remove( s:syn_value, list_idx ) 481 endif 482 endfor 483 endif 484endfunction 485 486function! sqlcomplete#Map(type) 487 " Tell the SQL plugin what you want to complete 488 let b:sql_compl_type=a:type 489 " Record previous omnifunc, if the SQL completion 490 " is being used in conjunction with other filetype 491 " completion plugins 492 if &omnifunc != "" && &omnifunc != 'sqlcomplete#Complete' 493 " Record the previous omnifunc, the plugin 494 " will automatically set this back so that it 495 " does not interfere with other ftplugins settings 496 let b:sql_compl_savefunc=&omnifunc 497 endif 498 " Set the OMNI func for the SQL completion plugin 499 let &omnifunc='sqlcomplete#Complete' 500endfunction 501 502function! sqlcomplete#DrillIntoTable() 503 " If the omni popup window is visible 504 if pumvisible() 505 call sqlcomplete#Map('column') 506 " C-Y, makes the currently highlighted entry active 507 " and trigger the omni popup to be redisplayed 508 call feedkeys("\<C-Y>\<C-X>\<C-O>", 'n') 509 else 510 " If the popup is not visible, simple perform the normal 511 " key behaviour. 512 " Must use exec since they key must be preceeded by "\" 513 " or feedkeys will simply push each character of the string 514 " rather than the "key press". 515 exec 'call feedkeys("\'.g:ftplugin_sql_omni_key_right.'", "n")' 516 endif 517 return "" 518endfunction 519 520function! sqlcomplete#DrillOutOfColumns() 521 " If the omni popup window is visible 522 if pumvisible() 523 call sqlcomplete#Map('tableReset') 524 " Trigger the omni popup to be redisplayed 525 call feedkeys("\<C-X>\<C-O>") 526 else 527 " If the popup is not visible, simple perform the normal 528 " key behaviour. 529 " Must use exec since they key must be preceeded by "\" 530 " or feedkeys will simply push each character of the string 531 " rather than the "key press". 532 exec 'call feedkeys("\'.g:ftplugin_sql_omni_key_left.'", "n")' 533 endif 534 return "" 535endfunction 536 537function! s:SQLCWarningMsg(msg) 538 echohl WarningMsg 539 echomsg 'SQLComplete:'.a:msg 540 echohl None 541endfunction 542 543function! s:SQLCErrorMsg(msg) 544 echohl ErrorMsg 545 echomsg 'SQLComplete:'.a:msg 546 echohl None 547endfunction 548 549function! s:SQLCGetSyntaxList(syn_group) 550 let syn_group = a:syn_group 551 let compl_list = [] 552 553 " Check if we have already cached the syntax list 554 let list_idx = index(s:syn_list, syn_group, 0, &ignorecase) 555 if list_idx > -1 556 " Return previously cached value 557 let compl_list = s:syn_value[list_idx] 558 else 559 " Request the syntax list items from the 560 " syntax completion plugin 561 if syn_group == 'syntax' 562 " Handle this special case. This allows the user 563 " to indicate they want all the syntax items available, 564 " so do not specify a specific include list. 565 let g:omni_syntax_group_include_sql = '' 566 else 567 " The user has specified a specific syntax group 568 let g:omni_syntax_group_include_sql = syn_group 569 endif 570 let g:omni_syntax_group_exclude_sql = '' 571 let syn_value = syntaxcomplete#OmniSyntaxList() 572 let g:omni_syntax_group_include_sql = s:save_inc 573 let g:omni_syntax_group_exclude_sql = s:save_exc 574 " Cache these values for later use 575 let s:syn_list = add( s:syn_list, syn_group ) 576 let s:syn_value = add( s:syn_value, syn_value ) 577 let compl_list = syn_value 578 endif 579 580 return compl_list 581endfunction 582 583function! s:SQLCCheck4dbext() 584 if !exists('g:loaded_dbext') 585 let msg = "The dbext plugin must be loaded for dynamic SQL completion" 586 call s:SQLCErrorMsg(msg) 587 " Leave time for the user to read the error message 588 :sleep 2 589 return -1 590 elseif g:loaded_dbext < 600 591 let msg = "The dbext plugin must be at least version 5.30 " . 592 \ " for dynamic SQL completion" 593 call s:SQLCErrorMsg(msg) 594 " Leave time for the user to read the error message 595 :sleep 2 596 return -1 597 endif 598 return 1 599endfunction 600 601function! s:SQLCAddAlias(table_name, table_alias, cols) 602 " Strip off the owner if included 603 let table_name = matchstr(a:table_name, '\%(.\{-}\.\)\?\zs\(.*\)' ) 604 let table_alias = a:table_alias 605 let cols = a:cols 606 607 if g:omni_sql_use_tbl_alias != 'n' 608 if table_alias == '' 609 if 'da' =~? g:omni_sql_use_tbl_alias 610 if table_name =~ '_' 611 " Treat _ as separators since people often use these 612 " for word separators 613 let save_keyword = &iskeyword 614 setlocal iskeyword-=_ 615 616 " Get the first letter of each word 617 " [[:alpha:]] is used instead of \w 618 " to catch extended accented characters 619 " 620 let table_alias = substitute( 621 \ table_name, 622 \ '\<[[:alpha:]]\+\>_\?', 623 \ '\=strpart(submatch(0), 0, 1)', 624 \ 'g' 625 \ ) 626 " Restore original value 627 let &iskeyword = save_keyword 628 elseif table_name =~ '\u\U' 629 let table_alias = substitute( 630 \ table_name, '\(\u\)\U*', '\1', 'g') 631 else 632 let table_alias = strpart(table_name, 0, 1) 633 endif 634 endif 635 endif 636 if table_alias != '' 637 " Following a word character, make sure there is a . and no spaces 638 let table_alias = substitute(table_alias, '\w\zs\.\?\s*$', '.', '') 639 if 'a' =~? g:omni_sql_use_tbl_alias && a:table_alias == '' 640 let table_alias = inputdialog("Enter table alias:", table_alias) 641 endif 642 endif 643 if table_alias != '' 644 let cols = substitute(cols, '\<\w', table_alias.'&', 'g') 645 endif 646 endif 647 648 return cols 649endfunction 650 651function! s:SQLCGetObjectOwner(object) 652 " The owner regex matches a word at the start of the string which is 653 " followed by a dot, but doesn't include the dot in the result. 654 " ^ - from beginning of line 655 " \("\|\[\)\? - ignore any quotes 656 " \zs - start the match now 657 " .\{-} - get owner name 658 " \ze - end the match 659 " \("\|\[\)\? - ignore any quotes 660 " \. - must by followed by a . 661 " let owner = matchstr( a:object, '^\s*\zs.*\ze\.' ) 662 let owner = matchstr( a:object, '^\("\|\[\)\?\zs\.\{-}\ze\("\|\]\)\?\.' ) 663 return owner 664endfunction 665 666function! s:SQLCGetColumns(table_name, list_type) 667 " Check if the table name was provided as part of the column name 668 let table_name = matchstr(a:table_name, '^["\[\]a-zA-Z0-9_ ]\+\ze\.\?') 669 let table_cols = [] 670 let table_alias = '' 671 let move_to_top = 1 672 673 let table_name = substitute(table_name, '\s*\(.\{-}\)\s*$', '\1', 'g') 674 675 " If the table name was given as: 676 " where c. 677 let table_name = substitute(table_name, '^\c\(WHERE\|AND\|OR\)\s\+', '', '') 678 if g:loaded_dbext >= 300 679 let saveSettingAlias = DB_listOption('use_tbl_alias') 680 exec 'DBSetOption use_tbl_alias=n' 681 endif 682 683 let table_name_stripped = substitute(table_name, '["\[\]]*', '', 'g') 684 685 " Check if we have already cached the column list for this table 686 " by its name 687 let list_idx = index(s:tbl_name, table_name_stripped, 0, &ignorecase) 688 if list_idx > -1 689 let table_cols = split(s:tbl_cols[list_idx], '\n') 690 else 691 " Check if we have already cached the column list for this table 692 " by its alias, assuming the table_name provided was actually 693 " the alias for the table instead 694 " select * 695 " from area a 696 " where a. 697 let list_idx = index(s:tbl_alias, table_name_stripped, 0, &ignorecase) 698 if list_idx > -1 699 let table_alias = table_name_stripped 700 let table_name = s:tbl_name[list_idx] 701 let table_cols = split(s:tbl_cols[list_idx], '\n') 702 endif 703 endif 704 705 " If we have not found a cached copy of the table 706 " And the table ends in a "." or we are looking for a column list 707 " if list_idx == -1 && (a:table_name =~ '\.' || b:sql_compl_type =~ 'column') 708 " if list_idx == -1 && (a:table_name =~ '\.' || a:list_type =~ 'csv') 709 if list_idx == -1 710 let saveY = @y 711 let saveSearch = @/ 712 let saveWScan = &wrapscan 713 let curline = line(".") 714 let curcol = col(".") 715 716 " Do not let searchs wrap 717 setlocal nowrapscan 718 " If . was entered, look at the word just before the . 719 " We are looking for something like this: 720 " select * 721 " from customer c 722 " where c. 723 " So when . is pressed, we need to find 'c' 724 " 725 726 " Search backwards to the beginning of the statement 727 " and do NOT wrap 728 " exec 'silent! normal! v?\<\(select\|update\|delete\|;\)\>'."\n".'"yy' 729 exec 'silent! normal! ?\<\c\(select\|update\|delete\|;\)\>'."\n" 730 731 " Start characterwise visual mode 732 " Advance right one character 733 " Search foward until one of the following: 734 " 1. Another select/update/delete statement 735 " 2. A ; at the end of a line (the delimiter) 736 " 3. The end of the file (incase no delimiter) 737 " Yank the visually selected text into the "y register. 738 exec 'silent! normal! vl/\c\(\<select\>\|\<update\>\|\<delete\>\|;\s*$\|\%$\)'."\n".'"yy' 739 740 let query = @y 741 let query = substitute(query, "\n", ' ', 'g') 742 let found = 0 743 744 " if query =~? '^\c\(select\)' 745 if query =~? '^\(select\|update\|delete\)' 746 let found = 1 747 " \(\(\<\w\+\>\)\.\)\? - 748 " '\c\(from\|join\|,\).\{-}' - Starting at the from clause (case insensitive) 749 " '\zs\(\(\<\w\+\>\)\.\)\?' - Get the owner name (optional) 750 " '\<\w\+\>\ze' - Get the table name 751 " '\s\+\<'.table_name.'\>' - Followed by the alias 752 " '\s*\.\@!.*' - Cannot be followed by a . 753 " '\(\<where\>\|$\)' - Must be followed by a WHERE clause 754 " '.*' - Exclude the rest of the line in the match 755 " let table_name_new = matchstr(@y, 756 " \ '\c\(from\|join\|,\).\{-}'. 757 " \ '\zs\(\("\|\[\)\?.\{-}\("\|\]\)\.\)\?'. 758 " \ '\("\|\[\)\?.\{-}\("\|\]\)\?\ze'. 759 " \ '\s\+\%(as\s\+\)\?\<'. 760 " \ matchstr(table_name, '.\{-}\ze\.\?$'). 761 " \ '\>'. 762 " \ '\s*\.\@!.*'. 763 " \ '\(\<where\>\|$\)'. 764 " \ '.*' 765 " \ ) 766 " 767 " 768 " ''\c\(\<from\>\|\<join\>\|,\)\s*' - Starting at the from clause (case insensitive) 769 " '\zs\(\("\|\[\)\?\w\+\("\|\]\)\?\.\)\?' - Get the owner name (optional) 770 " '\("\|\[\)\?\w\+\("\|\]\)\?\ze' - Get the table name 771 " '\s\+\%(as\s\+\)\?\<'.matchstr(table_name, '.\{-}\ze\.\?$').'\>' - Followed by the alias 772 " '\s*\.\@!.*' - Cannot be followed by a . 773 " '\(\<where\>\|$\)' - Must be followed by a WHERE clause 774 " '.*' - Exclude the rest of the line in the match 775 let table_name_new = matchstr(@y, 776 \ '\c\(\<from\>\|\<join\>\|,\)\s*'. 777 \ '\zs\(\("\|\[\)\?\w\+\("\|\]\)\?\.\)\?'. 778 \ '\("\|\[\)\?\w\+\("\|\]\)\?\ze'. 779 \ '\s\+\%(as\s\+\)\?\<'. 780 \ matchstr(table_name, '.\{-}\ze\.\?$'). 781 \ '\>'. 782 \ '\s*\.\@!.*'. 783 \ '\(\<where\>\|$\)'. 784 \ '.*' 785 \ ) 786 787 if table_name_new != '' 788 let table_alias = table_name 789 let table_name = matchstr( table_name_new, '^\(.*\.\)\?\zs.*\ze' ) 790 791 let list_idx = index(s:tbl_name, table_name, 0, &ignorecase) 792 if list_idx > -1 793 let table_cols = split(s:tbl_cols[list_idx]) 794 let s:tbl_name[list_idx] = table_name 795 let s:tbl_alias[list_idx] = table_alias 796 else 797 let list_idx = index(s:tbl_alias, table_name, 0, &ignorecase) 798 if list_idx > -1 799 let table_cols = split(s:tbl_cols[list_idx]) 800 let s:tbl_name[list_idx] = table_name 801 let s:tbl_alias[list_idx] = table_alias 802 endif 803 endif 804 805 endif 806 else 807 " Simply assume it is a table name provided with a . on the end 808 let found = 1 809 endif 810 811 let @y = saveY 812 let @/ = saveSearch 813 let &wrapscan = saveWScan 814 815 " Return to previous location 816 call cursor(curline, curcol) 817 818 if found == 0 819 if g:loaded_dbext > 300 820 exec 'DBSetOption use_tbl_alias='.saveSettingAlias 821 endif 822 823 " Not a SQL statement, do not display a list 824 return [] 825 endif 826 endif 827 828 if empty(table_cols) 829 " Specify silent mode, no messages to the user (tbl, 1) 830 " Specify do not comma separate (tbl, 1, 1) 831 let table_cols_str = DB_getListColumn(table_name, 1, 1) 832 833 if table_cols_str != "" 834 let s:tbl_name = add( s:tbl_name, table_name ) 835 let s:tbl_alias = add( s:tbl_alias, table_alias ) 836 let s:tbl_cols = add( s:tbl_cols, table_cols_str ) 837 let table_cols = split(table_cols_str, '\n') 838 endif 839 840 endif 841 842 if g:loaded_dbext > 300 843 exec 'DBSetOption use_tbl_alias='.saveSettingAlias 844 endif 845 846 " If the user has asked for a comma separate list of column 847 " values, ask the user if they want to prepend each column 848 " with a tablename alias. 849 if a:list_type == 'csv' && !empty(table_cols) 850 let cols = join(table_cols, ', ') 851 let cols = s:SQLCAddAlias(table_name, table_alias, cols) 852 let table_cols = [cols] 853 endif 854 855 return table_cols 856endfunction 857