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