1f193fffdSBram Moolenaar" Vim OMNI completion script for SQL
2e2f98b95SBram Moolenaar" Language:    SQL
35c73622aSBram Moolenaar" Maintainer:  David Fishburn <dfishburn dot vim at gmail dot com>
4*34feacbcSBram Moolenaar" Version:     14.0
5*34feacbcSBram Moolenaar" Last Change: 2012 Dec 04
6*34feacbcSBram Moolenaar" Homepage:    http://www.vim.org/scripts/script.php?script_id=1572
7f193fffdSBram Moolenaar" Usage:       For detailed help
8f193fffdSBram Moolenaar"              ":help sql.txt"
9f193fffdSBram Moolenaar"              or ":help ft-sql-omni"
10f193fffdSBram Moolenaar"              or read $VIMRUNTIME/doc/sql.txt
11e2f98b95SBram Moolenaar
125c73622aSBram Moolenaar" History
13*34feacbcSBram Moolenaar"
14*34feacbcSBram Moolenaar" Version 14.0 (Dec 2012)
15*34feacbcSBram Moolenaar"     - BF: Added check for cpo
16*34feacbcSBram Moolenaar"
17*34feacbcSBram Moolenaar" Version 13.0 (Dec 2012)
18*34feacbcSBram Moolenaar"     - NF: When completing column lists or drilling into a table
19*34feacbcSBram Moolenaar"           and g:omni_sql_include_owner is enabled, the
20*34feacbcSBram Moolenaar"           only the table name would be replaced with the column
21*34feacbcSBram Moolenaar"           list instead of the table name and owner (if specified).
22*34feacbcSBram Moolenaar"     - NF: When completing column lists using table aliases
23*34feacbcSBram Moolenaar"           and g:omni_sql_include_owner is enabled, account
24*34feacbcSBram Moolenaar"           for the owner name when looking up the table
25*34feacbcSBram Moolenaar"           list instead of the table name and owner (if specified).
26*34feacbcSBram Moolenaar"     - BF: When completing column lists or drilling into a table
27*34feacbcSBram Moolenaar"           and g:omni_sql_include_owner is enabled, the
28*34feacbcSBram Moolenaar"           column list could often not be found for the table.
29*34feacbcSBram Moolenaar"     - BF: When OMNI popped up, possibly the wrong word
30*34feacbcSBram Moolenaar"           would be replaced for column and column list options.
31*34feacbcSBram Moolenaar"
32*34feacbcSBram Moolenaar" Version 12.0 (Feb 2012)
33db7207e6SBram Moolenaar"     - Partial column name completion did not work when a table
34db7207e6SBram Moolenaar"       name or table alias was provided (Jonas Enberg).
35db7207e6SBram Moolenaar"     - Improved the handling of column completion.  First we match any
36db7207e6SBram Moolenaar"       columns from a previous completion.  If not matches are found, we
37db7207e6SBram Moolenaar"       consider the partial name to be a table or table alias for the
38db7207e6SBram Moolenaar"       query and attempt to match on it.
39db7207e6SBram Moolenaar"
40*34feacbcSBram Moolenaar" Version 11.0 (Jan 2012)
41db7207e6SBram Moolenaar"     Added g:omni_sql_default_compl_type variable
42db7207e6SBram Moolenaar"         - You can specify which type of completion to default to
43db7207e6SBram Moolenaar"           when pressing <C-X><C-O>.  The entire list of available
44db7207e6SBram Moolenaar"           choices can be found in the calls to sqlcomplete#Map in:
45db7207e6SBram Moolenaar"               ftplugin/sql.vim
46db7207e6SBram Moolenaar"
47f9d5ca1dSBram Moolenaar" Version 10.0
48f9d5ca1dSBram Moolenaar"     Updated PreCacheSyntax()
49f9d5ca1dSBram Moolenaar"         - Now returns a List of the syntax items it finds.
50f9d5ca1dSBram Moolenaar"           This allows other plugins / scripts to use this list for their own
51f9d5ca1dSBram Moolenaar"           purposes.  In this case XPTemplate can use them for a Choose list.
52f9d5ca1dSBram Moolenaar"         - Verifies the parameters are the correct type and displays a
53f9d5ca1dSBram Moolenaar"           warning if not.
54f9d5ca1dSBram Moolenaar"         - Verifies the parameters are the correct type and displays a
55f9d5ca1dSBram Moolenaar"           warning if not.
56f9d5ca1dSBram Moolenaar"     Updated SQLCWarningMsg()
57f9d5ca1dSBram Moolenaar"         - Prepends warning message with SQLComplete so you know who issued
58f9d5ca1dSBram Moolenaar"           the warning.
59f9d5ca1dSBram Moolenaar"     Updated SQLCErrorMsg()
60f9d5ca1dSBram Moolenaar"         - Prepends error message with SQLComplete so you know who issued
61f9d5ca1dSBram Moolenaar"           the error.
62f9d5ca1dSBram Moolenaar"
63*34feacbcSBram Moolenaar" Version 9.0 (May 2010)
6400a927d6SBram Moolenaar"     This change removes some of the support for tables with spaces in their
6500a927d6SBram Moolenaar"     names in order to simplify the regexes used to pull out query table
6600a927d6SBram Moolenaar"     aliases for more robust table name and column name code completion.
6700a927d6SBram Moolenaar"     Full support for "table names with spaces" can be added in again
6800a927d6SBram Moolenaar"     after 7.3.
69f9d5ca1dSBram Moolenaar"
7000a927d6SBram Moolenaar" Version 8.0
7100a927d6SBram Moolenaar"     Incorrectly re-executed the g:ftplugin_sql_omni_key_right and g:ftplugin_sql_omni_key_left
7200a927d6SBram Moolenaar"     when drilling in and out of a column list for a table.
73f9d5ca1dSBram Moolenaar"
74*34feacbcSBram Moolenaar" Version 7.0 (Jan 2010)
755c73622aSBram Moolenaar"     Better handling of object names
76f9d5ca1dSBram Moolenaar"
77*34feacbcSBram Moolenaar" Version 6.0 (Apr 2008)
785c73622aSBram Moolenaar"     Supports object names with spaces "my table name"
795c73622aSBram Moolenaar"
80e2f98b95SBram Moolenaar" Set completion with CTRL-X CTRL-O to autoloaded function.
81e2f98b95SBram Moolenaar" This check is in place in case this script is
82e2f98b95SBram Moolenaar" sourced directly instead of using the autoload feature.
83e2f98b95SBram Moolenaarif exists('&omnifunc')
84e2f98b95SBram Moolenaar    " Do not set the option if already set since this
85e2f98b95SBram Moolenaar    " results in an E117 warning.
86e2f98b95SBram Moolenaar    if &omnifunc == ""
87e2f98b95SBram Moolenaar        setlocal omnifunc=sqlcomplete#Complete
88e2f98b95SBram Moolenaar    endif
89e2f98b95SBram Moolenaarendif
90e2f98b95SBram Moolenaar
91e2f98b95SBram Moolenaarif exists('g:loaded_sql_completion')
92e2f98b95SBram Moolenaar    finish
93e2f98b95SBram Moolenaarendif
94*34feacbcSBram Moolenaarlet g:loaded_sql_completion = 130
95*34feacbcSBram Moolenaarlet s:keepcpo= &cpo
96*34feacbcSBram Moolenaarset cpo&vim
97e2f98b95SBram Moolenaar
98e2f98b95SBram Moolenaar" Maintains filename of dictionary
99e2f98b95SBram Moolenaarlet s:sql_file_table        = ""
100e2f98b95SBram Moolenaarlet s:sql_file_procedure    = ""
101e2f98b95SBram Moolenaarlet s:sql_file_view         = ""
102e2f98b95SBram Moolenaar
103e2f98b95SBram Moolenaar" Define various arrays to be used for caching
104e2f98b95SBram Moolenaarlet s:tbl_name              = []
105e2f98b95SBram Moolenaarlet s:tbl_alias             = []
106e2f98b95SBram Moolenaarlet s:tbl_cols              = []
107e2f98b95SBram Moolenaarlet s:syn_list              = []
108e2f98b95SBram Moolenaarlet s:syn_value             = []
109e2f98b95SBram Moolenaar
110e2f98b95SBram Moolenaar" Used in conjunction with the syntaxcomplete plugin
111e2f98b95SBram Moolenaarlet s:save_inc              = ""
112e2f98b95SBram Moolenaarlet s:save_exc              = ""
113e2f98b95SBram Moolenaarif exists('g:omni_syntax_group_include_sql')
114e2f98b95SBram Moolenaar    let s:save_inc = g:omni_syntax_group_include_sql
115e2f98b95SBram Moolenaarendif
116e2f98b95SBram Moolenaarif exists('g:omni_syntax_group_exclude_sql')
117e2f98b95SBram Moolenaar    let s:save_exc = g:omni_syntax_group_exclude_sql
118e2f98b95SBram Moolenaarendif
119e2f98b95SBram Moolenaar
120e2f98b95SBram Moolenaar" Used with the column list
121e2f98b95SBram Moolenaarlet s:save_prev_table       = ""
122e2f98b95SBram Moolenaar
123e2f98b95SBram Moolenaar" Default the option to verify table alias
124e2f98b95SBram Moolenaarif !exists('g:omni_sql_use_tbl_alias')
125e2f98b95SBram Moolenaar    let g:omni_sql_use_tbl_alias = 'a'
126e2f98b95SBram Moolenaarendif
127910f66f9SBram Moolenaar" Default syntax items to precache
128910f66f9SBram Moolenaarif !exists('g:omni_sql_precache_syntax_groups')
129910f66f9SBram Moolenaar    let g:omni_sql_precache_syntax_groups = [
130910f66f9SBram Moolenaar                \ 'syntax',
131910f66f9SBram Moolenaar                \ 'sqlKeyword',
132910f66f9SBram Moolenaar                \ 'sqlFunction',
133910f66f9SBram Moolenaar                \ 'sqlOption',
134910f66f9SBram Moolenaar                \ 'sqlType',
135910f66f9SBram Moolenaar                \ 'sqlStatement'
136910f66f9SBram Moolenaar                \ ]
137910f66f9SBram Moolenaarendif
138eb3593b3SBram Moolenaar" Set ignorecase to the ftplugin standard
139eb3593b3SBram Moolenaarif !exists('g:omni_sql_ignorecase')
140eb3593b3SBram Moolenaar    let g:omni_sql_ignorecase = &ignorecase
141eb3593b3SBram Moolenaarendif
142eb3593b3SBram Moolenaar" During table completion, should the table list also
143eb3593b3SBram Moolenaar" include the owner name
144eb3593b3SBram Moolenaarif !exists('g:omni_sql_include_owner')
145eb3593b3SBram Moolenaar    let g:omni_sql_include_owner = 0
146eb3593b3SBram Moolenaar    if exists('g:loaded_dbext')
147eb3593b3SBram Moolenaar        if g:loaded_dbext >= 300
148eb3593b3SBram Moolenaar            " New to dbext 3.00, by default the table lists include the owner
149eb3593b3SBram Moolenaar            " name of the table.  This is used when determining how much of
150eb3593b3SBram Moolenaar            " whatever has been typed should be replaced as part of the
151eb3593b3SBram Moolenaar            " code replacement.
152eb3593b3SBram Moolenaar            let g:omni_sql_include_owner = 1
153eb3593b3SBram Moolenaar        endif
154eb3593b3SBram Moolenaar    endif
155eb3593b3SBram Moolenaarendif
156db7207e6SBram Moolenaar" Default type of completion used when <C-X><C-O> is pressed
157db7207e6SBram Moolenaarif !exists('g:omni_sql_default_compl_type')
158db7207e6SBram Moolenaar    let g:omni_sql_default_compl_type = 'table'
159db7207e6SBram Moolenaarendif
160e2f98b95SBram Moolenaar
161e2f98b95SBram Moolenaar" This function is used for the 'omnifunc' option.
162*34feacbcSBram Moolenaar" It is called twice by omni and it is responsible
163*34feacbcSBram Moolenaar" for returning the completion list of items.
164*34feacbcSBram Moolenaar" But it must also determine context of what to complete
165*34feacbcSBram Moolenaar" and what to "replace" with the completion.
166*34feacbcSBram Moolenaar" The a:base, is replaced directly with what the user
167*34feacbcSBram Moolenaar" chooses from the choices.
168*34feacbcSBram Moolenaar" The s:prepend provides context for the completion.
169e2f98b95SBram Moolenaarfunction! sqlcomplete#Complete(findstart, base)
170e2f98b95SBram Moolenaar
171e2f98b95SBram Moolenaar    " Default to table name completion
172e2f98b95SBram Moolenaar    let compl_type = 'table'
173e2f98b95SBram Moolenaar    " Allow maps to specify what type of object completion they want
174e2f98b95SBram Moolenaar    if exists('b:sql_compl_type')
175e2f98b95SBram Moolenaar        let compl_type = b:sql_compl_type
176e2f98b95SBram Moolenaar    endif
177*34feacbcSBram Moolenaar    let begindot = 0
178e2f98b95SBram Moolenaar
179910f66f9SBram Moolenaar    " First pass through this function determines how much of the line should
180910f66f9SBram Moolenaar    " be replaced by whatever is chosen from the completion list
181e2f98b95SBram Moolenaar    if a:findstart
182e2f98b95SBram Moolenaar        " Locate the start of the item, including "."
183e2f98b95SBram Moolenaar        let line     = getline('.')
184e2f98b95SBram Moolenaar        let start    = col('.') - 1
185e2f98b95SBram Moolenaar        let lastword = -1
186f193fffdSBram Moolenaar        " Check if the first character is a ".", for column completion
187f193fffdSBram Moolenaar        if line[start - 1] == '.'
188f193fffdSBram Moolenaar            let begindot = 1
189f193fffdSBram Moolenaar        endif
190e2f98b95SBram Moolenaar        while start > 0
1915c73622aSBram Moolenaar            " Additional code was required to handle objects which
1925c73622aSBram Moolenaar            " can contain spaces like "my table name".
1935c73622aSBram Moolenaar            if line[start - 1] !~ '\(\w\|\.\)'
1945c73622aSBram Moolenaar                " If the previous character is not a period or word character
1955c73622aSBram Moolenaar                break
1965c73622aSBram Moolenaar            " elseif line[start - 1] =~ '\(\w\|\s\+\)'
1975c73622aSBram Moolenaar            "     let start -= 1
1985c73622aSBram Moolenaar            elseif line[start - 1] =~ '\w'
1995c73622aSBram Moolenaar                " If the previous character is word character continue back
200e2f98b95SBram Moolenaar                let start -= 1
201eb3593b3SBram Moolenaar            elseif line[start - 1] =~ '\.' &&
202eb3593b3SBram Moolenaar                        \ compl_type =~ 'column\|table\|view\|procedure'
2035c73622aSBram Moolenaar                " If the previous character is a period and we are completing
2045c73622aSBram Moolenaar                " an object which can be specified with a period like this:
2055c73622aSBram Moolenaar                "     table_name.column_name
2065c73622aSBram Moolenaar                "     owner_name.table_name
2075c73622aSBram Moolenaar
208eb3593b3SBram Moolenaar                " If lastword has already been set for column completion
209eb3593b3SBram Moolenaar                " break from the loop, since we do not also want to pickup
210eb3593b3SBram Moolenaar                " a table name if it was also supplied.
211*34feacbcSBram Moolenaar                " Unless g:omni_sql_include_owner == 1, then we can
212*34feacbcSBram Moolenaar                " include the ownername.
213f193fffdSBram Moolenaar                if lastword != -1 && compl_type == 'column'
214*34feacbcSBram Moolenaar                            \ && g:omni_sql_include_owner == 0
215eb3593b3SBram Moolenaar                    break
216eb3593b3SBram Moolenaar                endif
217f193fffdSBram Moolenaar                " If column completion was specified stop at the "." if
218f193fffdSBram Moolenaar                " a . was specified, otherwise, replace all the way up
219f193fffdSBram Moolenaar                " to the owner name (if included).
220f193fffdSBram Moolenaar                if lastword == -1 && compl_type == 'column' && begindot == 1
221e2f98b95SBram Moolenaar                    let lastword = start
222e2f98b95SBram Moolenaar                endif
223eb3593b3SBram Moolenaar                " If omni_sql_include_owner = 0, do not include the table
224eb3593b3SBram Moolenaar                " name as part of the substitution, so break here
225eb3593b3SBram Moolenaar                if lastword == -1 &&
226*34feacbcSBram Moolenaar                            \ compl_type =~ '\<\(table\|view\|procedure\|column\|column_csv\)\>' &&
227eb3593b3SBram Moolenaar                            \ g:omni_sql_include_owner == 0
228eb3593b3SBram Moolenaar                    let lastword = start
229eb3593b3SBram Moolenaar                    break
230eb3593b3SBram Moolenaar                endif
231e2f98b95SBram Moolenaar                let start -= 1
232e2f98b95SBram Moolenaar            else
233e2f98b95SBram Moolenaar                break
234e2f98b95SBram Moolenaar            endif
235e2f98b95SBram Moolenaar        endwhile
236e2f98b95SBram Moolenaar
237e2f98b95SBram Moolenaar        " Return the column of the last word, which is going to be changed.
238e2f98b95SBram Moolenaar        " Remember the text that comes before it in s:prepended.
239e2f98b95SBram Moolenaar        if lastword == -1
240e2f98b95SBram Moolenaar            let s:prepended = ''
241e2f98b95SBram Moolenaar            return start
242e2f98b95SBram Moolenaar        endif
243e2f98b95SBram Moolenaar        let s:prepended = strpart(line, start, lastword - start)
244e2f98b95SBram Moolenaar        return lastword
245e2f98b95SBram Moolenaar    endif
246e2f98b95SBram Moolenaar
247910f66f9SBram Moolenaar    " Second pass through this function will determine what data to put inside
248910f66f9SBram Moolenaar    " of the completion list
249910f66f9SBram Moolenaar    " s:prepended is set by the first pass
250e2f98b95SBram Moolenaar    let base = s:prepended . a:base
251e2f98b95SBram Moolenaar
252910f66f9SBram Moolenaar    " Default the completion list to an empty list
253e2f98b95SBram Moolenaar    let compl_list = []
254e2f98b95SBram Moolenaar
255e2f98b95SBram Moolenaar    " Default to table name completion
256db7207e6SBram Moolenaar    let compl_type = g:omni_sql_default_compl_type
257e2f98b95SBram Moolenaar    " Allow maps to specify what type of object completion they want
258e2f98b95SBram Moolenaar    if exists('b:sql_compl_type')
259e2f98b95SBram Moolenaar        let compl_type = b:sql_compl_type
260e2f98b95SBram Moolenaar        unlet b:sql_compl_type
261e2f98b95SBram Moolenaar    endif
262e2f98b95SBram Moolenaar
263e2f98b95SBram Moolenaar    if compl_type == 'tableReset'
264e2f98b95SBram Moolenaar        let compl_type = 'table'
265e2f98b95SBram Moolenaar        let base = ''
266e2f98b95SBram Moolenaar    endif
267e2f98b95SBram Moolenaar
268e2f98b95SBram Moolenaar    if compl_type == 'table' ||
269e2f98b95SBram Moolenaar                \ compl_type == 'procedure' ||
270e2f98b95SBram Moolenaar                \ compl_type == 'view'
271e2f98b95SBram Moolenaar
272e2f98b95SBram Moolenaar        " This type of completion relies upon the dbext.vim plugin
273e2f98b95SBram Moolenaar        if s:SQLCCheck4dbext() == -1
274e2f98b95SBram Moolenaar            return []
275e2f98b95SBram Moolenaar        endif
276e2f98b95SBram Moolenaar
27783e138c6SBram Moolenaar        " Allow the user to override the dbext plugin to specify whether
27883e138c6SBram Moolenaar        " the owner/creator should be included in the list
2798c8de839SBram Moolenaar        if g:loaded_dbext >= 300
2808c8de839SBram Moolenaar            let saveSetting = DB_listOption('dict_show_owner')
2818c8de839SBram Moolenaar            exec 'DBSetOption dict_show_owner='.(g:omni_sql_include_owner==1?'1':'0')
282e2f98b95SBram Moolenaar        endif
28383e138c6SBram Moolenaar
28483e138c6SBram Moolenaar        let compl_type_uc = substitute(compl_type, '\w\+', '\u&', '')
2855c73622aSBram Moolenaar        " Same call below, no need to do it twice
2865c73622aSBram Moolenaar        " if s:sql_file_{compl_type} == ""
2875c73622aSBram Moolenaar        "     let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type_uc)
2885c73622aSBram Moolenaar        " endif
28983e138c6SBram Moolenaar        let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type_uc)
290e2f98b95SBram Moolenaar        if s:sql_file_{compl_type} != ""
291e2f98b95SBram Moolenaar            if filereadable(s:sql_file_{compl_type})
292e2f98b95SBram Moolenaar                let compl_list = readfile(s:sql_file_{compl_type})
293e2f98b95SBram Moolenaar            endif
294e2f98b95SBram Moolenaar        endif
29583e138c6SBram Moolenaar
2968c8de839SBram Moolenaar        if g:loaded_dbext > 300
2978c8de839SBram Moolenaar            exec 'DBSetOption dict_show_owner='.saveSetting
2988c8de839SBram Moolenaar        endif
29983e138c6SBram Moolenaar    elseif compl_type =~? 'column'
300e2f98b95SBram Moolenaar
301e2f98b95SBram Moolenaar        " This type of completion relies upon the dbext.vim plugin
302e2f98b95SBram Moolenaar        if s:SQLCCheck4dbext() == -1
303e2f98b95SBram Moolenaar            return []
304e2f98b95SBram Moolenaar        endif
305e2f98b95SBram Moolenaar
306e2f98b95SBram Moolenaar        if base == ""
307e2f98b95SBram Moolenaar            " The last time we displayed a column list we stored
308e2f98b95SBram Moolenaar            " the table name.  If the user selects a column list
309e2f98b95SBram Moolenaar            " without a table name of alias present, assume they want
310e2f98b95SBram Moolenaar            " the previous column list displayed.
311e2f98b95SBram Moolenaar            let base = s:save_prev_table
312e2f98b95SBram Moolenaar        endif
313e2f98b95SBram Moolenaar
31483e138c6SBram Moolenaar        let owner  = ''
31583e138c6SBram Moolenaar        let column = ''
31683e138c6SBram Moolenaar
31783e138c6SBram Moolenaar        if base =~ '\.'
31883e138c6SBram Moolenaar            " Check if the owner/creator has been specified
31983e138c6SBram Moolenaar            let owner  = matchstr( base, '^\zs.*\ze\..*\..*' )
32083e138c6SBram Moolenaar            let table  = matchstr( base, '^\(.*\.\)\?\zs.*\ze\..*' )
32183e138c6SBram Moolenaar            let column = matchstr( base, '.*\.\zs.*' )
32283e138c6SBram Moolenaar
323*34feacbcSBram Moolenaar            if g:omni_sql_include_owner == 1 && owner == '' && table != '' && column != ''
324*34feacbcSBram Moolenaar                let owner  = table
325*34feacbcSBram Moolenaar                let table  = column
326*34feacbcSBram Moolenaar                let column = ''
327*34feacbcSBram Moolenaar            endif
328*34feacbcSBram Moolenaar
32983e138c6SBram Moolenaar            " It is pretty well impossible to determine if the user
33083e138c6SBram Moolenaar            " has entered:
33183e138c6SBram Moolenaar            "    owner.table
33283e138c6SBram Moolenaar            "    table.column_prefix
33383e138c6SBram Moolenaar            " So there are a couple of things we can do to mitigate
33483e138c6SBram Moolenaar            " this issue.
33583e138c6SBram Moolenaar            "    1.  Check if the dbext plugin has the option turned
33683e138c6SBram Moolenaar            "        on to even allow owners
33783e138c6SBram Moolenaar            "    2.  Based on 1, if the user is showing a table list
33800a927d6SBram Moolenaar            "        and the DrillIntoTable (using <Right>) then
33983e138c6SBram Moolenaar            "        this will be owner.table.  In this case, we can
34083e138c6SBram Moolenaar            "        check to see the table.column exists in the
34183e138c6SBram Moolenaar            "        cached table list.  If it does, then we have
34283e138c6SBram Moolenaar            "        determined the user has actually chosen
34383e138c6SBram Moolenaar            "        owner.table, not table.column_prefix.
34483e138c6SBram Moolenaar            let found = -1
34583e138c6SBram Moolenaar            if g:omni_sql_include_owner == 1 && owner == ''
34683e138c6SBram Moolenaar                if filereadable(s:sql_file_table)
34783e138c6SBram Moolenaar                    let tbl_list = readfile(s:sql_file_table)
34883e138c6SBram Moolenaar                    let found    = index( tbl_list, ((table != '')?(table.'.'):'').column)
34983e138c6SBram Moolenaar                endif
35083e138c6SBram Moolenaar            endif
35183e138c6SBram Moolenaar            " If the table.column was found in the table list, we can safely assume
35283e138c6SBram Moolenaar            " the owner was not provided and shift the items appropriately.
35383e138c6SBram Moolenaar            " OR
35483e138c6SBram Moolenaar            " If the user has indicated not to use table owners at all and
35583e138c6SBram Moolenaar            " the base ends in a '.' we know they are not providing a column
35683e138c6SBram Moolenaar            " name, so we can shift the items appropriately.
357db7207e6SBram Moolenaar            " if found != -1 || (g:omni_sql_include_owner == 0 && base !~ '\.$')
358db7207e6SBram Moolenaar            "     let owner  = table
359db7207e6SBram Moolenaar            "     let table  = column
360db7207e6SBram Moolenaar            "     let column = ''
361db7207e6SBram Moolenaar            " endif
36283e138c6SBram Moolenaar        else
363db7207e6SBram Moolenaar            " If no "." was provided and the user asked for
364db7207e6SBram Moolenaar            " column level completion, first attempt the match
365db7207e6SBram Moolenaar            " on any previous column lists.  If the user asked
366db7207e6SBram Moolenaar            " for a list of columns comma separated, continue as usual.
367db7207e6SBram Moolenaar            if compl_type == 'column' && s:save_prev_table != ''
368db7207e6SBram Moolenaar                " The last time we displayed a column list we stored
369db7207e6SBram Moolenaar                " the table name.  If the user selects a column list
370db7207e6SBram Moolenaar                " without a table name of alias present, assume they want
371db7207e6SBram Moolenaar                " the previous column list displayed.
372db7207e6SBram Moolenaar                let table     = s:save_prev_table
373db7207e6SBram Moolenaar                let list_type = ''
374db7207e6SBram Moolenaar
375db7207e6SBram Moolenaar                let compl_list  = s:SQLCGetColumns(table, list_type)
376db7207e6SBram Moolenaar                if ! empty(compl_list)
377db7207e6SBram Moolenaar                    " If no column prefix has been provided and the table
378db7207e6SBram Moolenaar                    " name was provided, append it to each of the items
379db7207e6SBram Moolenaar                    " returned.
380db7207e6SBram Moolenaar                    let compl_list = filter(deepcopy(compl_list), 'v:val=~"^'.base.'"' )
381db7207e6SBram Moolenaar
382db7207e6SBram Moolenaar                    " If not empty, we have a match on columns
383db7207e6SBram Moolenaar                    " return the list
384db7207e6SBram Moolenaar                    if ! empty(compl_list)
385db7207e6SBram Moolenaar                        return compl_list
386db7207e6SBram Moolenaar                    endif
387db7207e6SBram Moolenaar                endif
388db7207e6SBram Moolenaar            endif
389db7207e6SBram Moolenaar            " Since no columns were found to match the base supplied
390db7207e6SBram Moolenaar            " assume the user is trying to complete the column list
391db7207e6SBram Moolenaar            " for a table (and or an alias to a table).
39283e138c6SBram Moolenaar            let table  = base
39383e138c6SBram Moolenaar        endif
39483e138c6SBram Moolenaar
39583e138c6SBram Moolenaar        " Get anything after the . and consider this the table name
39683e138c6SBram Moolenaar        " If an owner has been specified, then we must consider the
39783e138c6SBram Moolenaar        " base to be a partial column name
39883e138c6SBram Moolenaar        " let base  = matchstr( base, '^\(.*\.\)\?\zs.*' )
39983e138c6SBram Moolenaar
40083e138c6SBram Moolenaar        if table != ""
401e2f98b95SBram Moolenaar            let s:save_prev_table = base
40283e138c6SBram Moolenaar            let list_type         = ''
40383e138c6SBram Moolenaar
40483e138c6SBram Moolenaar            if compl_type == 'column_csv'
40583e138c6SBram Moolenaar                " Return one array element, with a comma separated
40683e138c6SBram Moolenaar                " list of values instead of multiple array entries
40783e138c6SBram Moolenaar                " for each column in the table.
40883e138c6SBram Moolenaar                let list_type     = 'csv'
40983e138c6SBram Moolenaar            endif
41083e138c6SBram Moolenaar
411*34feacbcSBram Moolenaar            " If we are including the OWNER for the objects, then for
412*34feacbcSBram Moolenaar            " table completion, if we have it, it should be included
413*34feacbcSBram Moolenaar            " as there can be the same table names in a database yet
414*34feacbcSBram Moolenaar            " with different owner names.
415*34feacbcSBram Moolenaar            if g:omni_sql_include_owner == 1 && owner != '' && table != ''
416*34feacbcSBram Moolenaar                let compl_list  = s:SQLCGetColumns(owner.'.'.table, list_type)
417*34feacbcSBram Moolenaar            else
41883e138c6SBram Moolenaar                let compl_list  = s:SQLCGetColumns(table, list_type)
419*34feacbcSBram Moolenaar            endif
420*34feacbcSBram Moolenaar
42183e138c6SBram Moolenaar            if column != ''
42283e138c6SBram Moolenaar                " If no column prefix has been provided and the table
42383e138c6SBram Moolenaar                " name was provided, append it to each of the items
42483e138c6SBram Moolenaar                " returned.
425db7207e6SBram Moolenaar                let compl_list = map(compl_list, 'table.".".v:val')
42683e138c6SBram Moolenaar                if owner != ''
42783e138c6SBram Moolenaar                    " If an owner has been provided append it to each of the
42883e138c6SBram Moolenaar                    " items returned.
429db7207e6SBram Moolenaar                    let compl_list = map(compl_list, 'owner.".".v:val')
43083e138c6SBram Moolenaar                endif
43183e138c6SBram Moolenaar            else
432e2f98b95SBram Moolenaar                let base = ''
433e2f98b95SBram Moolenaar            endif
434e2f98b95SBram Moolenaar
43583e138c6SBram Moolenaar            if compl_type == 'column_csv'
436e2f98b95SBram Moolenaar                " Join the column array into 1 single element array
437e2f98b95SBram Moolenaar                " but make the columns column separated
438e2f98b95SBram Moolenaar                let compl_list        = [join(compl_list, ', ')]
43983e138c6SBram Moolenaar            endif
440e2f98b95SBram Moolenaar        endif
441e2f98b95SBram Moolenaar    elseif compl_type == 'resetCache'
442e2f98b95SBram Moolenaar        " Reset all cached items
443e2f98b95SBram Moolenaar        let s:tbl_name           = []
444e2f98b95SBram Moolenaar        let s:tbl_alias          = []
445e2f98b95SBram Moolenaar        let s:tbl_cols           = []
446e2f98b95SBram Moolenaar        let s:syn_list           = []
447e2f98b95SBram Moolenaar        let s:syn_value          = []
448*34feacbcSBram Moolenaar        let s:sql_file_table     = ""
449*34feacbcSBram Moolenaar        let s:sql_file_procedure = ""
450*34feacbcSBram Moolenaar        let s:sql_file_view      = ""
451f193fffdSBram Moolenaar
452f193fffdSBram Moolenaar        let msg = "All SQL cached items have been removed."
453f193fffdSBram Moolenaar        call s:SQLCWarningMsg(msg)
454f193fffdSBram Moolenaar        " Leave time for the user to read the error message
455f193fffdSBram Moolenaar        :sleep 2
456e2f98b95SBram Moolenaar    else
457910f66f9SBram Moolenaar        let compl_list = s:SQLCGetSyntaxList(compl_type)
458e2f98b95SBram Moolenaar    endif
459e2f98b95SBram Moolenaar
460e2f98b95SBram Moolenaar    if base != ''
4615c73622aSBram Moolenaar        " Filter the list based on the first few characters the user entered.
4625c73622aSBram Moolenaar        " Check if the text matches at the beginning
463db7207e6SBram Moolenaar        "         \\(^.base.'\\)
4645c73622aSBram Moolenaar        " or
4655c73622aSBram Moolenaar        " Match to a owner.table or alias.column type match
466db7207e6SBram Moolenaar        "         ^\\(\\w\\+\\.\\)\\?'.base.'\\)
4675c73622aSBram Moolenaar        " or
4685c73622aSBram Moolenaar        " Handle names with spaces "my table name"
469db7207e6SBram Moolenaar        "         "\\(^'.base.'\\|^\\(\\w\\+\\.\\)\\?'.base.'\\)"'
470db7207e6SBram Moolenaar        "
4715c73622aSBram Moolenaar        let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|^\\(\\w\\+\\.\\)\\?'.base.'\\)"'
4725c73622aSBram Moolenaar        " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\)"'
4735c73622aSBram Moolenaar        " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|\\(\\.\\)\\?'.base.'\\)"'
4745c73622aSBram Moolenaar        " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|\\([^.]*\\)\\?'.base.'\\)"'
475eb3593b3SBram Moolenaar        let compl_list = filter(deepcopy(compl_list), expr)
476*34feacbcSBram Moolenaar
477*34feacbcSBram Moolenaar        if empty(compl_list) && compl_type == 'table' && base =~ '\.$'
478*34feacbcSBram Moolenaar            " It is possible we could be looking for column name completion
479*34feacbcSBram Moolenaar            " and the user simply hit C-X C-O to lets try it as well
480*34feacbcSBram Moolenaar            " since we had no hits with the tables.
481*34feacbcSBram Moolenaar            " If the base ends with a . it is hard to know if we are
482*34feacbcSBram Moolenaar            " completing table names or column names.
483*34feacbcSBram Moolenaar            let list_type = ''
484*34feacbcSBram Moolenaar
485*34feacbcSBram Moolenaar            let compl_list  = s:SQLCGetColumns(base, list_type)
486*34feacbcSBram Moolenaar        endif
487e2f98b95SBram Moolenaar    endif
488e2f98b95SBram Moolenaar
489910f66f9SBram Moolenaar    if exists('b:sql_compl_savefunc') && b:sql_compl_savefunc != ""
490910f66f9SBram Moolenaar        let &omnifunc = b:sql_compl_savefunc
491910f66f9SBram Moolenaar    endif
492910f66f9SBram Moolenaar
493*34feacbcSBram Moolenaar    if empty(compl_list)
494*34feacbcSBram Moolenaar        call s:SQLCWarningMsg( 'Could not find type['.compl_type.'] using prepend[.'.s:prepended.'] base['.a:base.']' )
495*34feacbcSBram Moolenaar    endif
496*34feacbcSBram Moolenaar
497e2f98b95SBram Moolenaar    return compl_list
498e2f98b95SBram Moolenaarendfunc
499e2f98b95SBram Moolenaar
500910f66f9SBram Moolenaarfunction! sqlcomplete#PreCacheSyntax(...)
501910f66f9SBram Moolenaar    let syn_group_arr = []
502f9d5ca1dSBram Moolenaar    let syn_items     = []
503f9d5ca1dSBram Moolenaar
504910f66f9SBram Moolenaar    if a:0 > 0
505f9d5ca1dSBram Moolenaar        if type(a:1) != 3
506f9d5ca1dSBram Moolenaar            call s:SQLCWarningMsg("Parameter is not a list. Example:['syntaxGroup1', 'syntaxGroup2']")
507f9d5ca1dSBram Moolenaar            return ''
508f9d5ca1dSBram Moolenaar        endif
509910f66f9SBram Moolenaar        let syn_group_arr = a:1
510910f66f9SBram Moolenaar    else
511910f66f9SBram Moolenaar        let syn_group_arr = g:omni_sql_precache_syntax_groups
512910f66f9SBram Moolenaar    endif
51383e138c6SBram Moolenaar    " For each group specified in the list, precache all
51483e138c6SBram Moolenaar    " the sytnax items.
515910f66f9SBram Moolenaar    if !empty(syn_group_arr)
516910f66f9SBram Moolenaar        for group_name in syn_group_arr
517f9d5ca1dSBram Moolenaar            let syn_items = extend( syn_items, s:SQLCGetSyntaxList(group_name) )
518f9d5ca1dSBram Moolenaar        endfor
519f9d5ca1dSBram Moolenaar    endif
520f9d5ca1dSBram Moolenaar
521f9d5ca1dSBram Moolenaar    return syn_items
522f9d5ca1dSBram Moolenaarendfunction
523f9d5ca1dSBram Moolenaar
524f9d5ca1dSBram Moolenaarfunction! sqlcomplete#ResetCacheSyntax(...)
525f9d5ca1dSBram Moolenaar    let syn_group_arr = []
526f9d5ca1dSBram Moolenaar
527f9d5ca1dSBram Moolenaar    if a:0 > 0
528f9d5ca1dSBram Moolenaar        if type(a:1) != 3
529f9d5ca1dSBram Moolenaar            call s:SQLCWarningMsg("Parameter is not a list. Example:['syntaxGroup1', 'syntaxGroup2']")
530f9d5ca1dSBram Moolenaar            return ''
531f9d5ca1dSBram Moolenaar        endif
532f9d5ca1dSBram Moolenaar        let syn_group_arr = a:1
533f9d5ca1dSBram Moolenaar    else
534f9d5ca1dSBram Moolenaar        let syn_group_arr = g:omni_sql_precache_syntax_groups
535f9d5ca1dSBram Moolenaar    endif
536f9d5ca1dSBram Moolenaar    " For each group specified in the list, precache all
537f9d5ca1dSBram Moolenaar    " the sytnax items.
538f9d5ca1dSBram Moolenaar    if !empty(syn_group_arr)
539f9d5ca1dSBram Moolenaar        for group_name in syn_group_arr
540f9d5ca1dSBram Moolenaar            let list_idx = index(s:syn_list, group_name, 0, &ignorecase)
541f9d5ca1dSBram Moolenaar            if list_idx > -1
542f9d5ca1dSBram Moolenaar                " Remove from list of groups
543f9d5ca1dSBram Moolenaar                call remove( s:syn_list, list_idx )
544f9d5ca1dSBram Moolenaar                " Remove from list of keywords
545f9d5ca1dSBram Moolenaar                call remove( s:syn_value, list_idx )
546f9d5ca1dSBram Moolenaar            endif
547910f66f9SBram Moolenaar        endfor
548910f66f9SBram Moolenaar    endif
549910f66f9SBram Moolenaarendfunction
550910f66f9SBram Moolenaar
551910f66f9SBram Moolenaarfunction! sqlcomplete#Map(type)
552910f66f9SBram Moolenaar    " Tell the SQL plugin what you want to complete
553910f66f9SBram Moolenaar    let b:sql_compl_type=a:type
554910f66f9SBram Moolenaar    " Record previous omnifunc, if the SQL completion
555910f66f9SBram Moolenaar    " is being used in conjunction with other filetype
556910f66f9SBram Moolenaar    " completion plugins
557910f66f9SBram Moolenaar    if &omnifunc != "" && &omnifunc != 'sqlcomplete#Complete'
558910f66f9SBram Moolenaar        " Record the previous omnifunc, the plugin
559910f66f9SBram Moolenaar        " will automatically set this back so that it
560910f66f9SBram Moolenaar        " does not interfere with other ftplugins settings
561910f66f9SBram Moolenaar        let b:sql_compl_savefunc=&omnifunc
562910f66f9SBram Moolenaar    endif
563910f66f9SBram Moolenaar    " Set the OMNI func for the SQL completion plugin
564910f66f9SBram Moolenaar    let &omnifunc='sqlcomplete#Complete'
565910f66f9SBram Moolenaarendfunction
566910f66f9SBram Moolenaar
567f193fffdSBram Moolenaarfunction! sqlcomplete#DrillIntoTable()
568f193fffdSBram Moolenaar    " If the omni popup window is visible
569f193fffdSBram Moolenaar    if pumvisible()
570f193fffdSBram Moolenaar        call sqlcomplete#Map('column')
571f193fffdSBram Moolenaar        " C-Y, makes the currently highlighted entry active
572f193fffdSBram Moolenaar        " and trigger the omni popup to be redisplayed
57300a927d6SBram Moolenaar        call feedkeys("\<C-Y>\<C-X>\<C-O>", 'n')
574f193fffdSBram Moolenaar    else
575f193fffdSBram Moolenaar	" If the popup is not visible, simple perform the normal
57600a927d6SBram Moolenaar	" key behaviour.
57700a927d6SBram Moolenaar	" Must use exec since they key must be preceeded by "\"
57800a927d6SBram Moolenaar	" or feedkeys will simply push each character of the string
57900a927d6SBram Moolenaar	" rather than the "key press".
58000a927d6SBram Moolenaar        exec 'call feedkeys("\'.g:ftplugin_sql_omni_key_right.'", "n")'
581f193fffdSBram Moolenaar    endif
582f193fffdSBram Moolenaar    return ""
583f193fffdSBram Moolenaarendfunction
584f193fffdSBram Moolenaar
585f193fffdSBram Moolenaarfunction! sqlcomplete#DrillOutOfColumns()
586f193fffdSBram Moolenaar    " If the omni popup window is visible
587f193fffdSBram Moolenaar    if pumvisible()
588f193fffdSBram Moolenaar        call sqlcomplete#Map('tableReset')
589f193fffdSBram Moolenaar        " Trigger the omni popup to be redisplayed
590f193fffdSBram Moolenaar        call feedkeys("\<C-X>\<C-O>")
591f193fffdSBram Moolenaar    else
592f193fffdSBram Moolenaar	" If the popup is not visible, simple perform the normal
59300a927d6SBram Moolenaar	" key behaviour.
59400a927d6SBram Moolenaar	" Must use exec since they key must be preceeded by "\"
59500a927d6SBram Moolenaar	" or feedkeys will simply push each character of the string
59600a927d6SBram Moolenaar	" rather than the "key press".
59700a927d6SBram Moolenaar        exec 'call feedkeys("\'.g:ftplugin_sql_omni_key_left.'", "n")'
598f193fffdSBram Moolenaar    endif
599f193fffdSBram Moolenaar    return ""
600f193fffdSBram Moolenaarendfunction
601f193fffdSBram Moolenaar
602f193fffdSBram Moolenaarfunction! s:SQLCWarningMsg(msg)
603f193fffdSBram Moolenaar    echohl WarningMsg
604f9d5ca1dSBram Moolenaar    echomsg 'SQLComplete:'.a:msg
605f193fffdSBram Moolenaar    echohl None
606f193fffdSBram Moolenaarendfunction
607f193fffdSBram Moolenaar
608f193fffdSBram Moolenaarfunction! s:SQLCErrorMsg(msg)
609f193fffdSBram Moolenaar    echohl ErrorMsg
610f9d5ca1dSBram Moolenaar    echomsg 'SQLComplete:'.a:msg
611f193fffdSBram Moolenaar    echohl None
612f193fffdSBram Moolenaarendfunction
613f193fffdSBram Moolenaar
614910f66f9SBram Moolenaarfunction! s:SQLCGetSyntaxList(syn_group)
615910f66f9SBram Moolenaar    let syn_group  = a:syn_group
616910f66f9SBram Moolenaar    let compl_list = []
617910f66f9SBram Moolenaar
618910f66f9SBram Moolenaar    " Check if we have already cached the syntax list
619910f66f9SBram Moolenaar    let list_idx = index(s:syn_list, syn_group, 0, &ignorecase)
620910f66f9SBram Moolenaar    if list_idx > -1
621910f66f9SBram Moolenaar        " Return previously cached value
622910f66f9SBram Moolenaar        let compl_list = s:syn_value[list_idx]
623910f66f9SBram Moolenaar    else
624910f66f9SBram Moolenaar        " Request the syntax list items from the
625910f66f9SBram Moolenaar        " syntax completion plugin
626910f66f9SBram Moolenaar        if syn_group == 'syntax'
627910f66f9SBram Moolenaar            " Handle this special case.  This allows the user
628910f66f9SBram Moolenaar            " to indicate they want all the syntax items available,
629910f66f9SBram Moolenaar            " so do not specify a specific include list.
630910f66f9SBram Moolenaar            let g:omni_syntax_group_include_sql = ''
631910f66f9SBram Moolenaar        else
632910f66f9SBram Moolenaar            " The user has specified a specific syntax group
633910f66f9SBram Moolenaar            let g:omni_syntax_group_include_sql = syn_group
634910f66f9SBram Moolenaar        endif
635910f66f9SBram Moolenaar        let g:omni_syntax_group_exclude_sql = ''
636f9d5ca1dSBram Moolenaar        let syn_value                       = syntaxcomplete#OmniSyntaxList()
637910f66f9SBram Moolenaar        let g:omni_syntax_group_include_sql = s:save_inc
638910f66f9SBram Moolenaar        let g:omni_syntax_group_exclude_sql = s:save_exc
639910f66f9SBram Moolenaar        " Cache these values for later use
640910f66f9SBram Moolenaar        let s:syn_list  = add( s:syn_list,  syn_group )
641910f66f9SBram Moolenaar        let s:syn_value = add( s:syn_value, syn_value )
642910f66f9SBram Moolenaar        let compl_list  = syn_value
643910f66f9SBram Moolenaar    endif
644910f66f9SBram Moolenaar
645910f66f9SBram Moolenaar    return compl_list
646910f66f9SBram Moolenaarendfunction
647910f66f9SBram Moolenaar
648e2f98b95SBram Moolenaarfunction! s:SQLCCheck4dbext()
649e2f98b95SBram Moolenaar    if !exists('g:loaded_dbext')
650e2f98b95SBram Moolenaar        let msg = "The dbext plugin must be loaded for dynamic SQL completion"
651e2f98b95SBram Moolenaar        call s:SQLCErrorMsg(msg)
652e2f98b95SBram Moolenaar        " Leave time for the user to read the error message
653e2f98b95SBram Moolenaar        :sleep 2
654e2f98b95SBram Moolenaar        return -1
6558c8de839SBram Moolenaar    elseif g:loaded_dbext < 600
6568c8de839SBram Moolenaar        let msg = "The dbext plugin must be at least version 5.30 " .
657e2f98b95SBram Moolenaar                    \ " for dynamic SQL completion"
658e2f98b95SBram Moolenaar        call s:SQLCErrorMsg(msg)
659e2f98b95SBram Moolenaar        " Leave time for the user to read the error message
660e2f98b95SBram Moolenaar        :sleep 2
661e2f98b95SBram Moolenaar        return -1
662e2f98b95SBram Moolenaar    endif
663e2f98b95SBram Moolenaar    return 1
664e2f98b95SBram Moolenaarendfunction
665e2f98b95SBram Moolenaar
666e2f98b95SBram Moolenaarfunction! s:SQLCAddAlias(table_name, table_alias, cols)
667f193fffdSBram Moolenaar    " Strip off the owner if included
668f193fffdSBram Moolenaar    let table_name  = matchstr(a:table_name, '\%(.\{-}\.\)\?\zs\(.*\)' )
669e2f98b95SBram Moolenaar    let table_alias = a:table_alias
670e2f98b95SBram Moolenaar    let cols        = a:cols
671e2f98b95SBram Moolenaar
672e2f98b95SBram Moolenaar    if g:omni_sql_use_tbl_alias != 'n'
673e2f98b95SBram Moolenaar        if table_alias == ''
674e2f98b95SBram Moolenaar            if 'da' =~? g:omni_sql_use_tbl_alias
675e2f98b95SBram Moolenaar                if table_name =~ '_'
676e2f98b95SBram Moolenaar                    " Treat _ as separators since people often use these
677e2f98b95SBram Moolenaar                    " for word separators
678e2f98b95SBram Moolenaar                    let save_keyword = &iskeyword
679e2f98b95SBram Moolenaar                    setlocal iskeyword-=_
680e2f98b95SBram Moolenaar
681e2f98b95SBram Moolenaar                    " Get the first letter of each word
682e2f98b95SBram Moolenaar                    " [[:alpha:]] is used instead of \w
683e2f98b95SBram Moolenaar                    " to catch extended accented characters
684e2f98b95SBram Moolenaar                    "
685e2f98b95SBram Moolenaar                    let table_alias = substitute(
686e2f98b95SBram Moolenaar                                \ table_name,
687e2f98b95SBram Moolenaar                                \ '\<[[:alpha:]]\+\>_\?',
688e2f98b95SBram Moolenaar                                \ '\=strpart(submatch(0), 0, 1)',
689e2f98b95SBram Moolenaar                                \ 'g'
690e2f98b95SBram Moolenaar                                \ )
691e2f98b95SBram Moolenaar                    " Restore original value
692e2f98b95SBram Moolenaar                    let &iskeyword = save_keyword
693e2f98b95SBram Moolenaar                elseif table_name =~ '\u\U'
694f193fffdSBram Moolenaar                    let table_alias = substitute(
695e2f98b95SBram Moolenaar                                \ table_name, '\(\u\)\U*', '\1', 'g')
696e2f98b95SBram Moolenaar                else
697e2f98b95SBram Moolenaar                    let table_alias = strpart(table_name, 0, 1)
698e2f98b95SBram Moolenaar                endif
699e2f98b95SBram Moolenaar            endif
700e2f98b95SBram Moolenaar        endif
701e2f98b95SBram Moolenaar        if table_alias != ''
702e2f98b95SBram Moolenaar            " Following a word character, make sure there is a . and no spaces
703e2f98b95SBram Moolenaar            let table_alias = substitute(table_alias, '\w\zs\.\?\s*$', '.', '')
704e2f98b95SBram Moolenaar            if 'a' =~? g:omni_sql_use_tbl_alias && a:table_alias == ''
705e2f98b95SBram Moolenaar                let table_alias = inputdialog("Enter table alias:", table_alias)
706e2f98b95SBram Moolenaar            endif
707e2f98b95SBram Moolenaar        endif
708e2f98b95SBram Moolenaar        if table_alias != ''
709e2f98b95SBram Moolenaar            let cols = substitute(cols, '\<\w', table_alias.'&', 'g')
710e2f98b95SBram Moolenaar        endif
711e2f98b95SBram Moolenaar    endif
712e2f98b95SBram Moolenaar
713e2f98b95SBram Moolenaar    return cols
714e2f98b95SBram Moolenaarendfunction
715e2f98b95SBram Moolenaar
71683e138c6SBram Moolenaarfunction! s:SQLCGetObjectOwner(object)
71783e138c6SBram Moolenaar    " The owner regex matches a word at the start of the string which is
71883e138c6SBram Moolenaar    " followed by a dot, but doesn't include the dot in the result.
71983e138c6SBram Moolenaar    " ^           - from beginning of line
7208c8de839SBram Moolenaar    " \("\|\[\)\? - ignore any quotes
72183e138c6SBram Moolenaar    " \zs         - start the match now
7228c8de839SBram Moolenaar    " .\{-}       - get owner name
72383e138c6SBram Moolenaar    " \ze         - end the match
7248c8de839SBram Moolenaar    " \("\|\[\)\? - ignore any quotes
72583e138c6SBram Moolenaar    " \.          - must by followed by a .
7268c8de839SBram Moolenaar    " let owner = matchstr( a:object, '^\s*\zs.*\ze\.' )
7278c8de839SBram Moolenaar    let owner = matchstr( a:object, '^\("\|\[\)\?\zs\.\{-}\ze\("\|\]\)\?\.' )
72883e138c6SBram Moolenaar    return owner
72983e138c6SBram Moolenaarendfunction
73083e138c6SBram Moolenaar
731e2f98b95SBram Moolenaarfunction! s:SQLCGetColumns(table_name, list_type)
732*34feacbcSBram Moolenaar    if a:table_name =~ '\.'
733*34feacbcSBram Moolenaar        " Check if the owner/creator has been specified
734*34feacbcSBram Moolenaar        let owner  = matchstr( a:table_name, '^\zs.*\ze\..*\..*' )
735*34feacbcSBram Moolenaar        let table  = matchstr( a:table_name, '^\(.*\.\)\?\zs.*\ze\..*' )
736*34feacbcSBram Moolenaar        let column = matchstr( a:table_name, '.*\.\zs.*' )
737*34feacbcSBram Moolenaar
738*34feacbcSBram Moolenaar        if g:omni_sql_include_owner == 1 && owner == '' && table != '' && column != ''
739*34feacbcSBram Moolenaar            let owner  = table
740*34feacbcSBram Moolenaar            let table  = column
741*34feacbcSBram Moolenaar            let column = ''
742*34feacbcSBram Moolenaar        endif
743*34feacbcSBram Moolenaar    else
744*34feacbcSBram Moolenaar        let owner  = ''
745*34feacbcSBram Moolenaar        let table  = matchstr(a:table_name, '^["\[\]a-zA-Z0-9_ ]\+\ze\.\?')
746*34feacbcSBram Moolenaar        let column = ''
747*34feacbcSBram Moolenaar    endif
748*34feacbcSBram Moolenaar
74983e138c6SBram Moolenaar    " Check if the table name was provided as part of the column name
750*34feacbcSBram Moolenaar    " let table_name   = matchstr(a:table_name, '^["\[\]a-zA-Z0-9_ ]\+\ze\.\?')
751*34feacbcSBram Moolenaar    let table_name   = table
752e2f98b95SBram Moolenaar    let table_cols   = []
753e2f98b95SBram Moolenaar    let table_alias  = ''
754e2f98b95SBram Moolenaar    let move_to_top  = 1
755e2f98b95SBram Moolenaar
7568c8de839SBram Moolenaar    let table_name   = substitute(table_name, '\s*\(.\{-}\)\s*$', '\1', 'g')
7578c8de839SBram Moolenaar
7588c8de839SBram Moolenaar    " If the table name was given as:
7598c8de839SBram Moolenaar    "     where c.
7608c8de839SBram Moolenaar    let table_name   = substitute(table_name, '^\c\(WHERE\|AND\|OR\)\s\+', '', '')
761eb3593b3SBram Moolenaar    if g:loaded_dbext >= 300
762e2f98b95SBram Moolenaar        let saveSettingAlias = DB_listOption('use_tbl_alias')
763e2f98b95SBram Moolenaar        exec 'DBSetOption use_tbl_alias=n'
764e2f98b95SBram Moolenaar    endif
765e2f98b95SBram Moolenaar
7668c8de839SBram Moolenaar    let table_name_stripped = substitute(table_name, '["\[\]]*', '', 'g')
7678c8de839SBram Moolenaar
768e2f98b95SBram Moolenaar    " Check if we have already cached the column list for this table
769e2f98b95SBram Moolenaar    " by its name
7708c8de839SBram Moolenaar    let list_idx = index(s:tbl_name, table_name_stripped, 0, &ignorecase)
771e2f98b95SBram Moolenaar    if list_idx > -1
7728c8de839SBram Moolenaar        let table_cols = split(s:tbl_cols[list_idx], '\n')
773e2f98b95SBram Moolenaar    else
774e2f98b95SBram Moolenaar        " Check if we have already cached the column list for this table
775e2f98b95SBram Moolenaar        " by its alias, assuming the table_name provided was actually
776e2f98b95SBram Moolenaar        " the alias for the table instead
777e2f98b95SBram Moolenaar        "     select *
778e2f98b95SBram Moolenaar        "       from area a
779e2f98b95SBram Moolenaar        "      where a.
7808c8de839SBram Moolenaar        let list_idx = index(s:tbl_alias, table_name_stripped, 0, &ignorecase)
781e2f98b95SBram Moolenaar        if list_idx > -1
7828c8de839SBram Moolenaar            let table_alias = table_name_stripped
783e2f98b95SBram Moolenaar            let table_name  = s:tbl_name[list_idx]
7848c8de839SBram Moolenaar            let table_cols  = split(s:tbl_cols[list_idx], '\n')
785e2f98b95SBram Moolenaar        endif
786e2f98b95SBram Moolenaar    endif
787e2f98b95SBram Moolenaar
788e2f98b95SBram Moolenaar    " If we have not found a cached copy of the table
789e2f98b95SBram Moolenaar    " And the table ends in a "." or we are looking for a column list
790e2f98b95SBram Moolenaar    " if list_idx == -1 && (a:table_name =~ '\.' || b:sql_compl_type =~ 'column')
791e2f98b95SBram Moolenaar    " if list_idx == -1 && (a:table_name =~ '\.' || a:list_type =~ 'csv')
792e2f98b95SBram Moolenaar    if list_idx == -1
793e2f98b95SBram Moolenaar         let saveY      = @y
794e2f98b95SBram Moolenaar         let saveSearch = @/
795e2f98b95SBram Moolenaar         let saveWScan  = &wrapscan
796e2f98b95SBram Moolenaar         let curline    = line(".")
797e2f98b95SBram Moolenaar         let curcol     = col(".")
798e2f98b95SBram Moolenaar
799e2f98b95SBram Moolenaar         " Do not let searchs wrap
800e2f98b95SBram Moolenaar         setlocal nowrapscan
801e2f98b95SBram Moolenaar         " If . was entered, look at the word just before the .
802e2f98b95SBram Moolenaar         " We are looking for something like this:
803e2f98b95SBram Moolenaar         "    select *
804e2f98b95SBram Moolenaar         "      from customer c
805e2f98b95SBram Moolenaar         "     where c.
806e2f98b95SBram Moolenaar         " So when . is pressed, we need to find 'c'
807e2f98b95SBram Moolenaar         "
808e2f98b95SBram Moolenaar
809e2f98b95SBram Moolenaar         " Search backwards to the beginning of the statement
810e2f98b95SBram Moolenaar         " and do NOT wrap
811e2f98b95SBram Moolenaar         " exec 'silent! normal! v?\<\(select\|update\|delete\|;\)\>'."\n".'"yy'
81200a927d6SBram Moolenaar         exec 'silent! normal! ?\<\c\(select\|update\|delete\|;\)\>'."\n"
813e2f98b95SBram Moolenaar
814e2f98b95SBram Moolenaar         " Start characterwise visual mode
815e2f98b95SBram Moolenaar         " Advance right one character
816e2f98b95SBram Moolenaar         " Search foward until one of the following:
817e2f98b95SBram Moolenaar         "     1.  Another select/update/delete statement
818e2f98b95SBram Moolenaar         "     2.  A ; at the end of a line (the delimiter)
819e2f98b95SBram Moolenaar         "     3.  The end of the file (incase no delimiter)
820e2f98b95SBram Moolenaar         " Yank the visually selected text into the "y register.
82100a927d6SBram Moolenaar         exec 'silent! normal! vl/\c\(\<select\>\|\<update\>\|\<delete\>\|;\s*$\|\%$\)'."\n".'"yy'
822e2f98b95SBram Moolenaar
823e2f98b95SBram Moolenaar         let query = @y
824e2f98b95SBram Moolenaar         let query = substitute(query, "\n", ' ', 'g')
825e2f98b95SBram Moolenaar         let found = 0
826e2f98b95SBram Moolenaar
82700a927d6SBram Moolenaar         " if query =~? '^\c\(select\)'
82800a927d6SBram Moolenaar         if query =~? '^\(select\|update\|delete\)'
829e2f98b95SBram Moolenaar             let found = 1
830e2f98b95SBram Moolenaar             "  \(\(\<\w\+\>\)\.\)\?   -
83100a927d6SBram Moolenaar             " '\c\(from\|join\|,\).\{-}'  - Starting at the from clause (case insensitive)
832e2f98b95SBram Moolenaar             " '\zs\(\(\<\w\+\>\)\.\)\?' - Get the owner name (optional)
833e2f98b95SBram Moolenaar             " '\<\w\+\>\ze' - Get the table name
834e2f98b95SBram Moolenaar             " '\s\+\<'.table_name.'\>' - Followed by the alias
835e2f98b95SBram Moolenaar             " '\s*\.\@!.*'  - Cannot be followed by a .
836e2f98b95SBram Moolenaar             " '\(\<where\>\|$\)' - Must be followed by a WHERE clause
837e2f98b95SBram Moolenaar             " '.*'  - Exclude the rest of the line in the match
83800a927d6SBram Moolenaar             " let table_name_new = matchstr(@y,
83900a927d6SBram Moolenaar             "             \ '\c\(from\|join\|,\).\{-}'.
84000a927d6SBram Moolenaar             "             \ '\zs\(\("\|\[\)\?.\{-}\("\|\]\)\.\)\?'.
84100a927d6SBram Moolenaar             "             \ '\("\|\[\)\?.\{-}\("\|\]\)\?\ze'.
84200a927d6SBram Moolenaar             "             \ '\s\+\%(as\s\+\)\?\<'.
84300a927d6SBram Moolenaar             "             \ matchstr(table_name, '.\{-}\ze\.\?$').
84400a927d6SBram Moolenaar             "             \ '\>'.
84500a927d6SBram Moolenaar             "             \ '\s*\.\@!.*'.
84600a927d6SBram Moolenaar             "             \ '\(\<where\>\|$\)'.
84700a927d6SBram Moolenaar             "             \ '.*'
84800a927d6SBram Moolenaar             "             \ )
849db7207e6SBram Moolenaar             "
850db7207e6SBram Moolenaar             "
851db7207e6SBram Moolenaar             " ''\c\(\<from\>\|\<join\>\|,\)\s*'  - Starting at the from clause (case insensitive)
852db7207e6SBram Moolenaar             " '\zs\(\("\|\[\)\?\w\+\("\|\]\)\?\.\)\?' - Get the owner name (optional)
853db7207e6SBram Moolenaar             " '\("\|\[\)\?\w\+\("\|\]\)\?\ze' - Get the table name
854db7207e6SBram Moolenaar             " '\s\+\%(as\s\+\)\?\<'.matchstr(table_name, '.\{-}\ze\.\?$').'\>' - Followed by the alias
855db7207e6SBram Moolenaar             " '\s*\.\@!.*'  - Cannot be followed by a .
856db7207e6SBram Moolenaar             " '\(\<where\>\|$\)' - Must be followed by a WHERE clause
857db7207e6SBram Moolenaar             " '.*'  - Exclude the rest of the line in the match
858e2f98b95SBram Moolenaar             let table_name_new = matchstr(@y,
85900a927d6SBram Moolenaar                         \ '\c\(\<from\>\|\<join\>\|,\)\s*'.
86000a927d6SBram Moolenaar                         \ '\zs\(\("\|\[\)\?\w\+\("\|\]\)\?\.\)\?'.
86100a927d6SBram Moolenaar                         \ '\("\|\[\)\?\w\+\("\|\]\)\?\ze'.
862f193fffdSBram Moolenaar                         \ '\s\+\%(as\s\+\)\?\<'.
863f193fffdSBram Moolenaar                         \ matchstr(table_name, '.\{-}\ze\.\?$').
864f193fffdSBram Moolenaar                         \ '\>'.
865e2f98b95SBram Moolenaar                         \ '\s*\.\@!.*'.
866e2f98b95SBram Moolenaar                         \ '\(\<where\>\|$\)'.
867e2f98b95SBram Moolenaar                         \ '.*'
868e2f98b95SBram Moolenaar                         \ )
8698c8de839SBram Moolenaar
870e2f98b95SBram Moolenaar             if table_name_new != ''
871e2f98b95SBram Moolenaar                 let table_alias = table_name
872*34feacbcSBram Moolenaar                 if g:omni_sql_include_owner == 1
873*34feacbcSBram Moolenaar                    let table_name  = matchstr( table_name_new, '^\zs\(.\{-}\.\)\?\(.\{-}\.\)\?.*\ze' )
874*34feacbcSBram Moolenaar                 else
875*34feacbcSBram Moolenaar                     " let table_name  = matchstr( table_name_new, '^\(.*\.\)\?\zs.*\ze' )
876*34feacbcSBram Moolenaar                    let table_name  = matchstr( table_name_new, '^\(.\{-}\.\)\?\zs\(.\{-}\.\)\?.*\ze' )
877*34feacbcSBram Moolenaar                 endif
878e2f98b95SBram Moolenaar
879e2f98b95SBram Moolenaar                 let list_idx = index(s:tbl_name, table_name, 0, &ignorecase)
880e2f98b95SBram Moolenaar                 if list_idx > -1
881e2f98b95SBram Moolenaar                     let table_cols  = split(s:tbl_cols[list_idx])
882e2f98b95SBram Moolenaar                     let s:tbl_name[list_idx]  = table_name
883e2f98b95SBram Moolenaar                     let s:tbl_alias[list_idx] = table_alias
884e2f98b95SBram Moolenaar                 else
885e2f98b95SBram Moolenaar                     let list_idx = index(s:tbl_alias, table_name, 0, &ignorecase)
886e2f98b95SBram Moolenaar                     if list_idx > -1
887e2f98b95SBram Moolenaar                         let table_cols = split(s:tbl_cols[list_idx])
888e2f98b95SBram Moolenaar                         let s:tbl_name[list_idx]  = table_name
889e2f98b95SBram Moolenaar                         let s:tbl_alias[list_idx] = table_alias
890e2f98b95SBram Moolenaar                     endif
891e2f98b95SBram Moolenaar                 endif
892e2f98b95SBram Moolenaar
893e2f98b95SBram Moolenaar             endif
894e2f98b95SBram Moolenaar         else
895e2f98b95SBram Moolenaar             " Simply assume it is a table name provided with a . on the end
896e2f98b95SBram Moolenaar             let found = 1
897e2f98b95SBram Moolenaar         endif
898e2f98b95SBram Moolenaar
899e2f98b95SBram Moolenaar         let @y        = saveY
900e2f98b95SBram Moolenaar         let @/        = saveSearch
901e2f98b95SBram Moolenaar         let &wrapscan = saveWScan
902e2f98b95SBram Moolenaar
903e2f98b95SBram Moolenaar         " Return to previous location
904e2f98b95SBram Moolenaar         call cursor(curline, curcol)
905e2f98b95SBram Moolenaar
906e2f98b95SBram Moolenaar         if found == 0
907eb3593b3SBram Moolenaar             if g:loaded_dbext > 300
908e2f98b95SBram Moolenaar                 exec 'DBSetOption use_tbl_alias='.saveSettingAlias
909e2f98b95SBram Moolenaar             endif
910e2f98b95SBram Moolenaar
911e2f98b95SBram Moolenaar             " Not a SQL statement, do not display a list
912e2f98b95SBram Moolenaar             return []
913e2f98b95SBram Moolenaar         endif
914e2f98b95SBram Moolenaar    endif
915e2f98b95SBram Moolenaar
916e2f98b95SBram Moolenaar    if empty(table_cols)
917e2f98b95SBram Moolenaar        " Specify silent mode, no messages to the user (tbl, 1)
918e2f98b95SBram Moolenaar        " Specify do not comma separate (tbl, 1, 1)
919*34feacbcSBram Moolenaar        " let table_cols_str = DB_getListColumn(table_name, 1, 1)
920*34feacbcSBram Moolenaar        let table_cols_str = DB_getListColumn((owner!=''?owner.'.':'').table_name, 1, 1)
921e2f98b95SBram Moolenaar
922e2f98b95SBram Moolenaar        if table_cols_str != ""
923e2f98b95SBram Moolenaar            let s:tbl_name  = add( s:tbl_name,  table_name )
924e2f98b95SBram Moolenaar            let s:tbl_alias = add( s:tbl_alias, table_alias )
925e2f98b95SBram Moolenaar            let s:tbl_cols  = add( s:tbl_cols,  table_cols_str )
9268c8de839SBram Moolenaar            let table_cols  = split(table_cols_str, '\n')
927e2f98b95SBram Moolenaar        endif
928e2f98b95SBram Moolenaar
929e2f98b95SBram Moolenaar    endif
930e2f98b95SBram Moolenaar
931eb3593b3SBram Moolenaar    if g:loaded_dbext > 300
932e2f98b95SBram Moolenaar        exec 'DBSetOption use_tbl_alias='.saveSettingAlias
933e2f98b95SBram Moolenaar    endif
934e2f98b95SBram Moolenaar
935f193fffdSBram Moolenaar    " If the user has asked for a comma separate list of column
936f193fffdSBram Moolenaar    " values, ask the user if they want to prepend each column
937f193fffdSBram Moolenaar    " with a tablename alias.
938e2f98b95SBram Moolenaar    if a:list_type == 'csv' && !empty(table_cols)
939e2f98b95SBram Moolenaar        let cols       = join(table_cols, ', ')
940e2f98b95SBram Moolenaar        let cols       = s:SQLCAddAlias(table_name, table_alias, cols)
941e2f98b95SBram Moolenaar        let table_cols = [cols]
942e2f98b95SBram Moolenaar    endif
943e2f98b95SBram Moolenaar
944e2f98b95SBram Moolenaar    return table_cols
945e2f98b95SBram Moolenaarendfunction
946*34feacbcSBram Moolenaar"  Restore:
947*34feacbcSBram Moolenaarlet &cpo= s:keepcpo
948*34feacbcSBram Moolenaarunlet s:keepcpo
949*34feacbcSBram Moolenaar" vim: ts=4 fdm=marker
950