home *** CD-ROM | disk | FTP | other *** search
/ vim.ftp.fu-berlin.de / 2015-02-03.vim.ftp.fu-berlin.de.tar / vim.ftp.fu-berlin.de / runtime / autoload / sqlcomplete.vim < prev    next >
Encoding:
Text File  |  2012-05-31  |  32.5 KB  |  857 lines

  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.
  63. if 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
  69. endif
  70.  
  71. if exists('g:loaded_sql_completion')
  72.     finish
  73. endif
  74. let g:loaded_sql_completion = 120
  75.  
  76. " Maintains filename of dictionary
  77. let s:sql_file_table        = ""
  78. let s:sql_file_procedure    = ""
  79. let s:sql_file_view         = ""
  80.  
  81. " Define various arrays to be used for caching
  82. let s:tbl_name              = []
  83. let s:tbl_alias             = []
  84. let s:tbl_cols              = []
  85. let s:syn_list              = []
  86. let s:syn_value             = []
  87.  
  88. " Used in conjunction with the syntaxcomplete plugin
  89. let s:save_inc              = ""
  90. let s:save_exc              = ""
  91. if exists('g:omni_syntax_group_include_sql')
  92.     let s:save_inc = g:omni_syntax_group_include_sql
  93. endif
  94. if exists('g:omni_syntax_group_exclude_sql')
  95.     let s:save_exc = g:omni_syntax_group_exclude_sql
  96. endif
  97.  
  98. " Used with the column list
  99. let s:save_prev_table       = ""
  100.  
  101. " Default the option to verify table alias
  102. if !exists('g:omni_sql_use_tbl_alias')
  103.     let g:omni_sql_use_tbl_alias = 'a'
  104. endif
  105. " Default syntax items to precache
  106. if !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.                 \ ]
  115. endif
  116. " Set ignorecase to the ftplugin standard
  117. if !exists('g:omni_sql_ignorecase')
  118.     let g:omni_sql_ignorecase = &ignorecase
  119. endif
  120. " During table completion, should the table list also
  121. " include the owner name
  122. if !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
  133. endif
  134. " Default type of completion used when <C-X><C-O> is pressed
  135. if !exists('g:omni_sql_default_compl_type')
  136.     let g:omni_sql_default_compl_type = 'table'
  137. endif
  138.  
  139. " This function is used for the 'omnifunc' option.
  140. function! 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
  433. endfunc
  434.  
  435. function! 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
  457. endfunction
  458.  
  459. function! 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
  484. endfunction
  485.  
  486. function! 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'
  500. endfunction
  501.  
  502. function! 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 ""
  518. endfunction
  519.  
  520. function! 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 ""
  535. endfunction
  536.  
  537. function! s:SQLCWarningMsg(msg)
  538.     echohl WarningMsg
  539.     echomsg 'SQLComplete:'.a:msg
  540.     echohl None
  541. endfunction
  542.  
  543. function! s:SQLCErrorMsg(msg)
  544.     echohl ErrorMsg
  545.     echomsg 'SQLComplete:'.a:msg
  546.     echohl None
  547. endfunction
  548.  
  549. function! 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
  581. endfunction
  582.  
  583. function! 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
  599. endfunction
  600.  
  601. function! 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
  649. endfunction
  650.  
  651. function! 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
  664. endfunction
  665.  
  666. function! 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
  856. endfunction
  857.