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*ad3b366cSBram Moolenaar" Version:     15.0
5*ad3b366cSBram Moolenaar" Last Change: 2013 May 13
634feacbcSBram 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
1334feacbcSBram Moolenaar"
14*ad3b366cSBram Moolenaar" TODO
15*ad3b366cSBram Moolenaar"     - Jonas Enberg - if no table is found when using column completion
16*ad3b366cSBram Moolenaar"       look backwards to a FROM clause and find the first table
17*ad3b366cSBram Moolenaar"       and complete it.
18*ad3b366cSBram Moolenaar"
19*ad3b366cSBram Moolenaar" Version 15.0 (May 2013)
20*ad3b366cSBram Moolenaar"     - NF: Changed the SQL precached syntax items, omni_sql_precache_syntax_groups,
21*ad3b366cSBram Moolenaar"           to use regular expressions to pick up extended syntax group names.
22*ad3b366cSBram Moolenaar"           This requires an updated SyntaxComplete plugin version 13.0.
23*ad3b366cSBram Moolenaar"           If the required versions have not been installed, previous
24*ad3b366cSBram Moolenaar"           behaviour will not be impacted.
25*ad3b366cSBram Moolenaar"
2634feacbcSBram Moolenaar" Version 14.0 (Dec 2012)
2734feacbcSBram Moolenaar"     - BF: Added check for cpo
2834feacbcSBram Moolenaar"
2934feacbcSBram Moolenaar" Version 13.0 (Dec 2012)
3034feacbcSBram Moolenaar"     - NF: When completing column lists or drilling into a table
3134feacbcSBram Moolenaar"           and g:omni_sql_include_owner is enabled, the
3234feacbcSBram Moolenaar"           only the table name would be replaced with the column
3334feacbcSBram Moolenaar"           list instead of the table name and owner (if specified).
3434feacbcSBram Moolenaar"     - NF: When completing column lists using table aliases
3534feacbcSBram Moolenaar"           and g:omni_sql_include_owner is enabled, account
3634feacbcSBram Moolenaar"           for the owner name when looking up the table
3734feacbcSBram Moolenaar"           list instead of the table name and owner (if specified).
3834feacbcSBram Moolenaar"     - BF: When completing column lists or drilling into a table
3934feacbcSBram Moolenaar"           and g:omni_sql_include_owner is enabled, the
4034feacbcSBram Moolenaar"           column list could often not be found for the table.
4134feacbcSBram Moolenaar"     - BF: When OMNI popped up, possibly the wrong word
4234feacbcSBram Moolenaar"           would be replaced for column and column list options.
4334feacbcSBram Moolenaar"
4434feacbcSBram Moolenaar" Version 12.0 (Feb 2012)
45db7207e6SBram Moolenaar"     - Partial column name completion did not work when a table
46db7207e6SBram Moolenaar"       name or table alias was provided (Jonas Enberg).
47db7207e6SBram Moolenaar"     - Improved the handling of column completion.  First we match any
48db7207e6SBram Moolenaar"       columns from a previous completion.  If not matches are found, we
49db7207e6SBram Moolenaar"       consider the partial name to be a table or table alias for the
50db7207e6SBram Moolenaar"       query and attempt to match on it.
51db7207e6SBram Moolenaar"
5234feacbcSBram Moolenaar" Version 11.0 (Jan 2012)
53db7207e6SBram Moolenaar"     Added g:omni_sql_default_compl_type variable
54db7207e6SBram Moolenaar"         - You can specify which type of completion to default to
55db7207e6SBram Moolenaar"           when pressing <C-X><C-O>.  The entire list of available
56db7207e6SBram Moolenaar"           choices can be found in the calls to sqlcomplete#Map in:
57db7207e6SBram Moolenaar"               ftplugin/sql.vim
58db7207e6SBram Moolenaar"
59f9d5ca1dSBram Moolenaar" Version 10.0
60f9d5ca1dSBram Moolenaar"     Updated PreCacheSyntax()
61f9d5ca1dSBram Moolenaar"         - Now returns a List of the syntax items it finds.
62f9d5ca1dSBram Moolenaar"           This allows other plugins / scripts to use this list for their own
63f9d5ca1dSBram Moolenaar"           purposes.  In this case XPTemplate can use them for a Choose list.
64f9d5ca1dSBram Moolenaar"         - Verifies the parameters are the correct type and displays a
65f9d5ca1dSBram Moolenaar"           warning if not.
66f9d5ca1dSBram Moolenaar"         - Verifies the parameters are the correct type and displays a
67f9d5ca1dSBram Moolenaar"           warning if not.
68f9d5ca1dSBram Moolenaar"     Updated SQLCWarningMsg()
69f9d5ca1dSBram Moolenaar"         - Prepends warning message with SQLComplete so you know who issued
70f9d5ca1dSBram Moolenaar"           the warning.
71f9d5ca1dSBram Moolenaar"     Updated SQLCErrorMsg()
72f9d5ca1dSBram Moolenaar"         - Prepends error message with SQLComplete so you know who issued
73f9d5ca1dSBram Moolenaar"           the error.
74f9d5ca1dSBram Moolenaar"
7534feacbcSBram Moolenaar" Version 9.0 (May 2010)
7600a927d6SBram Moolenaar"     This change removes some of the support for tables with spaces in their
7700a927d6SBram Moolenaar"     names in order to simplify the regexes used to pull out query table
7800a927d6SBram Moolenaar"     aliases for more robust table name and column name code completion.
7900a927d6SBram Moolenaar"     Full support for "table names with spaces" can be added in again
8000a927d6SBram Moolenaar"     after 7.3.
81f9d5ca1dSBram Moolenaar"
8200a927d6SBram Moolenaar" Version 8.0
8300a927d6SBram Moolenaar"     Incorrectly re-executed the g:ftplugin_sql_omni_key_right and g:ftplugin_sql_omni_key_left
8400a927d6SBram Moolenaar"     when drilling in and out of a column list for a table.
85f9d5ca1dSBram Moolenaar"
8634feacbcSBram Moolenaar" Version 7.0 (Jan 2010)
875c73622aSBram Moolenaar"     Better handling of object names
88f9d5ca1dSBram Moolenaar"
8934feacbcSBram Moolenaar" Version 6.0 (Apr 2008)
905c73622aSBram Moolenaar"     Supports object names with spaces "my table name"
915c73622aSBram Moolenaar"
92e2f98b95SBram Moolenaar" Set completion with CTRL-X CTRL-O to autoloaded function.
93e2f98b95SBram Moolenaar" This check is in place in case this script is
94e2f98b95SBram Moolenaar" sourced directly instead of using the autoload feature.
95e2f98b95SBram Moolenaarif exists('&omnifunc')
96e2f98b95SBram Moolenaar    " Do not set the option if already set since this
97e2f98b95SBram Moolenaar    " results in an E117 warning.
98e2f98b95SBram Moolenaar    if &omnifunc == ""
99e2f98b95SBram Moolenaar        setlocal omnifunc=sqlcomplete#Complete
100e2f98b95SBram Moolenaar    endif
101e2f98b95SBram Moolenaarendif
102e2f98b95SBram Moolenaar
103e2f98b95SBram Moolenaarif exists('g:loaded_sql_completion')
104e2f98b95SBram Moolenaar    finish
105e2f98b95SBram Moolenaarendif
106*ad3b366cSBram Moolenaarlet g:loaded_sql_completion = 150
10734feacbcSBram Moolenaarlet s:keepcpo= &cpo
10834feacbcSBram Moolenaarset cpo&vim
109e2f98b95SBram Moolenaar
110e2f98b95SBram Moolenaar" Maintains filename of dictionary
111e2f98b95SBram Moolenaarlet s:sql_file_table        = ""
112e2f98b95SBram Moolenaarlet s:sql_file_procedure    = ""
113e2f98b95SBram Moolenaarlet s:sql_file_view         = ""
114e2f98b95SBram Moolenaar
115e2f98b95SBram Moolenaar" Define various arrays to be used for caching
116e2f98b95SBram Moolenaarlet s:tbl_name              = []
117e2f98b95SBram Moolenaarlet s:tbl_alias             = []
118e2f98b95SBram Moolenaarlet s:tbl_cols              = []
119e2f98b95SBram Moolenaarlet s:syn_list              = []
120e2f98b95SBram Moolenaarlet s:syn_value             = []
121e2f98b95SBram Moolenaar
122e2f98b95SBram Moolenaar" Used in conjunction with the syntaxcomplete plugin
123e2f98b95SBram Moolenaarlet s:save_inc              = ""
124e2f98b95SBram Moolenaarlet s:save_exc              = ""
125*ad3b366cSBram Moolenaarif !exists('g:omni_syntax_group_include_sql')
126*ad3b366cSBram Moolenaar    let g:omni_syntax_group_include_sql = ''
127*ad3b366cSBram Moolenaarendif
128*ad3b366cSBram Moolenaarif !exists('g:omni_syntax_group_exclude_sql')
129*ad3b366cSBram Moolenaar    let g:omni_syntax_group_exclude_sql = ''
130*ad3b366cSBram Moolenaarendif
131e2f98b95SBram Moolenaarlet s:save_inc = g:omni_syntax_group_include_sql
132e2f98b95SBram Moolenaarlet s:save_exc = g:omni_syntax_group_exclude_sql
133e2f98b95SBram Moolenaar
134e2f98b95SBram Moolenaar" Used with the column list
135e2f98b95SBram Moolenaarlet s:save_prev_table       = ""
136e2f98b95SBram Moolenaar
137e2f98b95SBram Moolenaar" Default the option to verify table alias
138e2f98b95SBram Moolenaarif !exists('g:omni_sql_use_tbl_alias')
139e2f98b95SBram Moolenaar    let g:omni_sql_use_tbl_alias = 'a'
140e2f98b95SBram Moolenaarendif
141910f66f9SBram Moolenaar" Default syntax items to precache
142910f66f9SBram Moolenaarif !exists('g:omni_sql_precache_syntax_groups')
143910f66f9SBram Moolenaar    let g:omni_sql_precache_syntax_groups = [
144*ad3b366cSBram Moolenaar                \ 'syntax\w*',
145*ad3b366cSBram Moolenaar                \ 'sqlKeyword\w*',
146*ad3b366cSBram Moolenaar                \ 'sqlFunction\w*',
147*ad3b366cSBram Moolenaar                \ 'sqlOption\w*',
148*ad3b366cSBram Moolenaar                \ 'sqlType\w*',
149*ad3b366cSBram Moolenaar                \ 'sqlStatement\w*'
150910f66f9SBram Moolenaar                \ ]
151910f66f9SBram Moolenaarendif
152eb3593b3SBram Moolenaar" Set ignorecase to the ftplugin standard
153eb3593b3SBram Moolenaarif !exists('g:omni_sql_ignorecase')
154eb3593b3SBram Moolenaar    let g:omni_sql_ignorecase = &ignorecase
155eb3593b3SBram Moolenaarendif
156eb3593b3SBram Moolenaar" During table completion, should the table list also
157eb3593b3SBram Moolenaar" include the owner name
158eb3593b3SBram Moolenaarif !exists('g:omni_sql_include_owner')
159eb3593b3SBram Moolenaar    let g:omni_sql_include_owner = 0
160eb3593b3SBram Moolenaar    if exists('g:loaded_dbext')
161eb3593b3SBram Moolenaar        if g:loaded_dbext >= 300
162eb3593b3SBram Moolenaar            " New to dbext 3.00, by default the table lists include the owner
163eb3593b3SBram Moolenaar            " name of the table.  This is used when determining how much of
164eb3593b3SBram Moolenaar            " whatever has been typed should be replaced as part of the
165eb3593b3SBram Moolenaar            " code replacement.
166eb3593b3SBram Moolenaar            let g:omni_sql_include_owner = 1
167eb3593b3SBram Moolenaar        endif
168eb3593b3SBram Moolenaar    endif
169eb3593b3SBram Moolenaarendif
170db7207e6SBram Moolenaar" Default type of completion used when <C-X><C-O> is pressed
171db7207e6SBram Moolenaarif !exists('g:omni_sql_default_compl_type')
172db7207e6SBram Moolenaar    let g:omni_sql_default_compl_type = 'table'
173db7207e6SBram Moolenaarendif
174e2f98b95SBram Moolenaar
175e2f98b95SBram Moolenaar" This function is used for the 'omnifunc' option.
17634feacbcSBram Moolenaar" It is called twice by omni and it is responsible
17734feacbcSBram Moolenaar" for returning the completion list of items.
17834feacbcSBram Moolenaar" But it must also determine context of what to complete
17934feacbcSBram Moolenaar" and what to "replace" with the completion.
18034feacbcSBram Moolenaar" The a:base, is replaced directly with what the user
18134feacbcSBram Moolenaar" chooses from the choices.
18234feacbcSBram Moolenaar" The s:prepend provides context for the completion.
183e2f98b95SBram Moolenaarfunction! sqlcomplete#Complete(findstart, base)
184e2f98b95SBram Moolenaar
185e2f98b95SBram Moolenaar    " Default to table name completion
186e2f98b95SBram Moolenaar    let compl_type = 'table'
187e2f98b95SBram Moolenaar    " Allow maps to specify what type of object completion they want
188e2f98b95SBram Moolenaar    if exists('b:sql_compl_type')
189e2f98b95SBram Moolenaar        let compl_type = b:sql_compl_type
190e2f98b95SBram Moolenaar    endif
19134feacbcSBram Moolenaar    let begindot = 0
192e2f98b95SBram Moolenaar
193910f66f9SBram Moolenaar    " First pass through this function determines how much of the line should
194910f66f9SBram Moolenaar    " be replaced by whatever is chosen from the completion list
195e2f98b95SBram Moolenaar    if a:findstart
196e2f98b95SBram Moolenaar        " Locate the start of the item, including "."
197e2f98b95SBram Moolenaar        let line     = getline('.')
198e2f98b95SBram Moolenaar        let start    = col('.') - 1
199e2f98b95SBram Moolenaar        let lastword = -1
200f193fffdSBram Moolenaar        " Check if the first character is a ".", for column completion
201f193fffdSBram Moolenaar        if line[start - 1] == '.'
202f193fffdSBram Moolenaar            let begindot = 1
203f193fffdSBram Moolenaar        endif
204e2f98b95SBram Moolenaar        while start > 0
2055c73622aSBram Moolenaar            " Additional code was required to handle objects which
2065c73622aSBram Moolenaar            " can contain spaces like "my table name".
2075c73622aSBram Moolenaar            if line[start - 1] !~ '\(\w\|\.\)'
2085c73622aSBram Moolenaar                " If the previous character is not a period or word character
2095c73622aSBram Moolenaar                break
2105c73622aSBram Moolenaar            " elseif line[start - 1] =~ '\(\w\|\s\+\)'
2115c73622aSBram Moolenaar            "     let start -= 1
2125c73622aSBram Moolenaar            elseif line[start - 1] =~ '\w'
2135c73622aSBram Moolenaar                " If the previous character is word character continue back
214e2f98b95SBram Moolenaar                let start -= 1
215eb3593b3SBram Moolenaar            elseif line[start - 1] =~ '\.' &&
216eb3593b3SBram Moolenaar                        \ compl_type =~ 'column\|table\|view\|procedure'
2175c73622aSBram Moolenaar                " If the previous character is a period and we are completing
2185c73622aSBram Moolenaar                " an object which can be specified with a period like this:
2195c73622aSBram Moolenaar                "     table_name.column_name
2205c73622aSBram Moolenaar                "     owner_name.table_name
2215c73622aSBram Moolenaar
222eb3593b3SBram Moolenaar                " If lastword has already been set for column completion
223eb3593b3SBram Moolenaar                " break from the loop, since we do not also want to pickup
224eb3593b3SBram Moolenaar                " a table name if it was also supplied.
22534feacbcSBram Moolenaar                " Unless g:omni_sql_include_owner == 1, then we can
22634feacbcSBram Moolenaar                " include the ownername.
227f193fffdSBram Moolenaar                if lastword != -1 && compl_type == 'column'
22834feacbcSBram Moolenaar                            \ && g:omni_sql_include_owner == 0
229eb3593b3SBram Moolenaar                    break
230eb3593b3SBram Moolenaar                endif
231f193fffdSBram Moolenaar                " If column completion was specified stop at the "." if
232f193fffdSBram Moolenaar                " a . was specified, otherwise, replace all the way up
233f193fffdSBram Moolenaar                " to the owner name (if included).
234f193fffdSBram Moolenaar                if lastword == -1 && compl_type == 'column' && begindot == 1
235e2f98b95SBram Moolenaar                    let lastword = start
236e2f98b95SBram Moolenaar                endif
237eb3593b3SBram Moolenaar                " If omni_sql_include_owner = 0, do not include the table
238eb3593b3SBram Moolenaar                " name as part of the substitution, so break here
239eb3593b3SBram Moolenaar                if lastword == -1 &&
24034feacbcSBram Moolenaar                            \ compl_type =~ '\<\(table\|view\|procedure\|column\|column_csv\)\>' &&
241eb3593b3SBram Moolenaar                            \ g:omni_sql_include_owner == 0
242eb3593b3SBram Moolenaar                    let lastword = start
243eb3593b3SBram Moolenaar                    break
244eb3593b3SBram Moolenaar                endif
245e2f98b95SBram Moolenaar                let start -= 1
246e2f98b95SBram Moolenaar            else
247e2f98b95SBram Moolenaar                break
248e2f98b95SBram Moolenaar            endif
249e2f98b95SBram Moolenaar        endwhile
250e2f98b95SBram Moolenaar
251e2f98b95SBram Moolenaar        " Return the column of the last word, which is going to be changed.
252e2f98b95SBram Moolenaar        " Remember the text that comes before it in s:prepended.
253e2f98b95SBram Moolenaar        if lastword == -1
254e2f98b95SBram Moolenaar            let s:prepended = ''
255e2f98b95SBram Moolenaar            return start
256e2f98b95SBram Moolenaar        endif
257e2f98b95SBram Moolenaar        let s:prepended = strpart(line, start, lastword - start)
258e2f98b95SBram Moolenaar        return lastword
259e2f98b95SBram Moolenaar    endif
260e2f98b95SBram Moolenaar
261910f66f9SBram Moolenaar    " Second pass through this function will determine what data to put inside
262910f66f9SBram Moolenaar    " of the completion list
263910f66f9SBram Moolenaar    " s:prepended is set by the first pass
264e2f98b95SBram Moolenaar    let base = s:prepended . a:base
265e2f98b95SBram Moolenaar
266910f66f9SBram Moolenaar    " Default the completion list to an empty list
267e2f98b95SBram Moolenaar    let compl_list = []
268e2f98b95SBram Moolenaar
269e2f98b95SBram Moolenaar    " Default to table name completion
270db7207e6SBram Moolenaar    let compl_type = g:omni_sql_default_compl_type
271e2f98b95SBram Moolenaar    " Allow maps to specify what type of object completion they want
272e2f98b95SBram Moolenaar    if exists('b:sql_compl_type')
273e2f98b95SBram Moolenaar        let compl_type = b:sql_compl_type
274e2f98b95SBram Moolenaar        unlet b:sql_compl_type
275e2f98b95SBram Moolenaar    endif
276e2f98b95SBram Moolenaar
277e2f98b95SBram Moolenaar    if compl_type == 'tableReset'
278e2f98b95SBram Moolenaar        let compl_type = 'table'
279e2f98b95SBram Moolenaar        let base = ''
280e2f98b95SBram Moolenaar    endif
281e2f98b95SBram Moolenaar
282e2f98b95SBram Moolenaar    if compl_type == 'table' ||
283e2f98b95SBram Moolenaar                \ compl_type == 'procedure' ||
284e2f98b95SBram Moolenaar                \ compl_type == 'view'
285e2f98b95SBram Moolenaar
286e2f98b95SBram Moolenaar        " This type of completion relies upon the dbext.vim plugin
287e2f98b95SBram Moolenaar        if s:SQLCCheck4dbext() == -1
288e2f98b95SBram Moolenaar            return []
289e2f98b95SBram Moolenaar        endif
290e2f98b95SBram Moolenaar
29183e138c6SBram Moolenaar        " Allow the user to override the dbext plugin to specify whether
29283e138c6SBram Moolenaar        " the owner/creator should be included in the list
2938c8de839SBram Moolenaar        if g:loaded_dbext >= 300
2948c8de839SBram Moolenaar            let saveSetting = DB_listOption('dict_show_owner')
2958c8de839SBram Moolenaar            exec 'DBSetOption dict_show_owner='.(g:omni_sql_include_owner==1?'1':'0')
296e2f98b95SBram Moolenaar        endif
29783e138c6SBram Moolenaar
29883e138c6SBram Moolenaar        let compl_type_uc = substitute(compl_type, '\w\+', '\u&', '')
2995c73622aSBram Moolenaar        " Same call below, no need to do it twice
3005c73622aSBram Moolenaar        " if s:sql_file_{compl_type} == ""
3015c73622aSBram Moolenaar        "     let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type_uc)
3025c73622aSBram Moolenaar        " endif
30383e138c6SBram Moolenaar        let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type_uc)
304e2f98b95SBram Moolenaar        if s:sql_file_{compl_type} != ""
305e2f98b95SBram Moolenaar            if filereadable(s:sql_file_{compl_type})
306e2f98b95SBram Moolenaar                let compl_list = readfile(s:sql_file_{compl_type})
307e2f98b95SBram Moolenaar            endif
308e2f98b95SBram Moolenaar        endif
30983e138c6SBram Moolenaar
3108c8de839SBram Moolenaar        if g:loaded_dbext > 300
3118c8de839SBram Moolenaar            exec 'DBSetOption dict_show_owner='.saveSetting
3128c8de839SBram Moolenaar        endif
31383e138c6SBram Moolenaar    elseif compl_type =~? 'column'
314e2f98b95SBram Moolenaar
315e2f98b95SBram Moolenaar        " This type of completion relies upon the dbext.vim plugin
316e2f98b95SBram Moolenaar        if s:SQLCCheck4dbext() == -1
317e2f98b95SBram Moolenaar            return []
318e2f98b95SBram Moolenaar        endif
319e2f98b95SBram Moolenaar
320e2f98b95SBram Moolenaar        if base == ""
321e2f98b95SBram Moolenaar            " The last time we displayed a column list we stored
322e2f98b95SBram Moolenaar            " the table name.  If the user selects a column list
323e2f98b95SBram Moolenaar            " without a table name of alias present, assume they want
324e2f98b95SBram Moolenaar            " the previous column list displayed.
325e2f98b95SBram Moolenaar            let base = s:save_prev_table
326e2f98b95SBram Moolenaar        endif
327e2f98b95SBram Moolenaar
32883e138c6SBram Moolenaar        let owner  = ''
32983e138c6SBram Moolenaar        let column = ''
33083e138c6SBram Moolenaar
33183e138c6SBram Moolenaar        if base =~ '\.'
33283e138c6SBram Moolenaar            " Check if the owner/creator has been specified
33383e138c6SBram Moolenaar            let owner  = matchstr( base, '^\zs.*\ze\..*\..*' )
33483e138c6SBram Moolenaar            let table  = matchstr( base, '^\(.*\.\)\?\zs.*\ze\..*' )
33583e138c6SBram Moolenaar            let column = matchstr( base, '.*\.\zs.*' )
33683e138c6SBram Moolenaar
33734feacbcSBram Moolenaar            if g:omni_sql_include_owner == 1 && owner == '' && table != '' && column != ''
33834feacbcSBram Moolenaar                let owner  = table
33934feacbcSBram Moolenaar                let table  = column
34034feacbcSBram Moolenaar                let column = ''
34134feacbcSBram Moolenaar            endif
34234feacbcSBram Moolenaar
34383e138c6SBram Moolenaar            " It is pretty well impossible to determine if the user
34483e138c6SBram Moolenaar            " has entered:
34583e138c6SBram Moolenaar            "    owner.table
34683e138c6SBram Moolenaar            "    table.column_prefix
34783e138c6SBram Moolenaar            " So there are a couple of things we can do to mitigate
34883e138c6SBram Moolenaar            " this issue.
34983e138c6SBram Moolenaar            "    1.  Check if the dbext plugin has the option turned
35083e138c6SBram Moolenaar            "        on to even allow owners
35183e138c6SBram Moolenaar            "    2.  Based on 1, if the user is showing a table list
35200a927d6SBram Moolenaar            "        and the DrillIntoTable (using <Right>) then
35383e138c6SBram Moolenaar            "        this will be owner.table.  In this case, we can
35483e138c6SBram Moolenaar            "        check to see the table.column exists in the
35583e138c6SBram Moolenaar            "        cached table list.  If it does, then we have
35683e138c6SBram Moolenaar            "        determined the user has actually chosen
35783e138c6SBram Moolenaar            "        owner.table, not table.column_prefix.
35883e138c6SBram Moolenaar            let found = -1
35983e138c6SBram Moolenaar            if g:omni_sql_include_owner == 1 && owner == ''
36083e138c6SBram Moolenaar                if filereadable(s:sql_file_table)
36183e138c6SBram Moolenaar                    let tbl_list = readfile(s:sql_file_table)
36283e138c6SBram Moolenaar                    let found    = index( tbl_list, ((table != '')?(table.'.'):'').column)
36383e138c6SBram Moolenaar                endif
36483e138c6SBram Moolenaar            endif
36583e138c6SBram Moolenaar            " If the table.column was found in the table list, we can safely assume
36683e138c6SBram Moolenaar            " the owner was not provided and shift the items appropriately.
36783e138c6SBram Moolenaar            " OR
36883e138c6SBram Moolenaar            " If the user has indicated not to use table owners at all and
36983e138c6SBram Moolenaar            " the base ends in a '.' we know they are not providing a column
37083e138c6SBram Moolenaar            " name, so we can shift the items appropriately.
371db7207e6SBram Moolenaar            " if found != -1 || (g:omni_sql_include_owner == 0 && base !~ '\.$')
372db7207e6SBram Moolenaar            "     let owner  = table
373db7207e6SBram Moolenaar            "     let table  = column
374db7207e6SBram Moolenaar            "     let column = ''
375db7207e6SBram Moolenaar            " endif
37683e138c6SBram Moolenaar        else
377db7207e6SBram Moolenaar            " If no "." was provided and the user asked for
378db7207e6SBram Moolenaar            " column level completion, first attempt the match
379db7207e6SBram Moolenaar            " on any previous column lists.  If the user asked
380db7207e6SBram Moolenaar            " for a list of columns comma separated, continue as usual.
381db7207e6SBram Moolenaar            if compl_type == 'column' && s:save_prev_table != ''
382db7207e6SBram Moolenaar                " The last time we displayed a column list we stored
383db7207e6SBram Moolenaar                " the table name.  If the user selects a column list
384db7207e6SBram Moolenaar                " without a table name of alias present, assume they want
385db7207e6SBram Moolenaar                " the previous column list displayed.
386db7207e6SBram Moolenaar                let table     = s:save_prev_table
387db7207e6SBram Moolenaar                let list_type = ''
388db7207e6SBram Moolenaar
389db7207e6SBram Moolenaar                let compl_list  = s:SQLCGetColumns(table, list_type)
390db7207e6SBram Moolenaar                if ! empty(compl_list)
391db7207e6SBram Moolenaar                    " If no column prefix has been provided and the table
392db7207e6SBram Moolenaar                    " name was provided, append it to each of the items
393db7207e6SBram Moolenaar                    " returned.
394db7207e6SBram Moolenaar                    let compl_list = filter(deepcopy(compl_list), 'v:val=~"^'.base.'"' )
395db7207e6SBram Moolenaar
396db7207e6SBram Moolenaar                    " If not empty, we have a match on columns
397db7207e6SBram Moolenaar                    " return the list
398db7207e6SBram Moolenaar                    if ! empty(compl_list)
399db7207e6SBram Moolenaar                        return compl_list
400db7207e6SBram Moolenaar                    endif
401db7207e6SBram Moolenaar                endif
402db7207e6SBram Moolenaar            endif
403db7207e6SBram Moolenaar            " Since no columns were found to match the base supplied
404db7207e6SBram Moolenaar            " assume the user is trying to complete the column list
405db7207e6SBram Moolenaar            " for a table (and or an alias to a table).
40683e138c6SBram Moolenaar            let table  = base
40783e138c6SBram Moolenaar        endif
40883e138c6SBram Moolenaar
40983e138c6SBram Moolenaar        " Get anything after the . and consider this the table name
41083e138c6SBram Moolenaar        " If an owner has been specified, then we must consider the
41183e138c6SBram Moolenaar        " base to be a partial column name
41283e138c6SBram Moolenaar        " let base  = matchstr( base, '^\(.*\.\)\?\zs.*' )
41383e138c6SBram Moolenaar
41483e138c6SBram Moolenaar        if table != ""
415e2f98b95SBram Moolenaar            let s:save_prev_table = base
41683e138c6SBram Moolenaar            let list_type         = ''
41783e138c6SBram Moolenaar
41883e138c6SBram Moolenaar            if compl_type == 'column_csv'
41983e138c6SBram Moolenaar                " Return one array element, with a comma separated
42083e138c6SBram Moolenaar                " list of values instead of multiple array entries
42183e138c6SBram Moolenaar                " for each column in the table.
42283e138c6SBram Moolenaar                let list_type     = 'csv'
42383e138c6SBram Moolenaar            endif
42483e138c6SBram Moolenaar
42534feacbcSBram Moolenaar            " If we are including the OWNER for the objects, then for
42634feacbcSBram Moolenaar            " table completion, if we have it, it should be included
42734feacbcSBram Moolenaar            " as there can be the same table names in a database yet
42834feacbcSBram Moolenaar            " with different owner names.
42934feacbcSBram Moolenaar            if g:omni_sql_include_owner == 1 && owner != '' && table != ''
43034feacbcSBram Moolenaar                let compl_list  = s:SQLCGetColumns(owner.'.'.table, list_type)
43134feacbcSBram Moolenaar            else
43283e138c6SBram Moolenaar                let compl_list  = s:SQLCGetColumns(table, list_type)
43334feacbcSBram Moolenaar            endif
43434feacbcSBram Moolenaar
43583e138c6SBram Moolenaar            if column != ''
43683e138c6SBram Moolenaar                " If no column prefix has been provided and the table
43783e138c6SBram Moolenaar                " name was provided, append it to each of the items
43883e138c6SBram Moolenaar                " returned.
439db7207e6SBram Moolenaar                let compl_list = map(compl_list, 'table.".".v:val')
44083e138c6SBram Moolenaar                if owner != ''
44183e138c6SBram Moolenaar                    " If an owner has been provided append it to each of the
44283e138c6SBram Moolenaar                    " items returned.
443db7207e6SBram Moolenaar                    let compl_list = map(compl_list, 'owner.".".v:val')
44483e138c6SBram Moolenaar                endif
44583e138c6SBram Moolenaar            else
446e2f98b95SBram Moolenaar                let base = ''
447e2f98b95SBram Moolenaar            endif
448e2f98b95SBram Moolenaar
44983e138c6SBram Moolenaar            if compl_type == 'column_csv'
450e2f98b95SBram Moolenaar                " Join the column array into 1 single element array
451e2f98b95SBram Moolenaar                " but make the columns column separated
452e2f98b95SBram Moolenaar                let compl_list        = [join(compl_list, ', ')]
45383e138c6SBram Moolenaar            endif
454e2f98b95SBram Moolenaar        endif
455e2f98b95SBram Moolenaar    elseif compl_type == 'resetCache'
456e2f98b95SBram Moolenaar        " Reset all cached items
457e2f98b95SBram Moolenaar        let s:tbl_name           = []
458e2f98b95SBram Moolenaar        let s:tbl_alias          = []
459e2f98b95SBram Moolenaar        let s:tbl_cols           = []
460e2f98b95SBram Moolenaar        let s:syn_list           = []
461e2f98b95SBram Moolenaar        let s:syn_value          = []
46234feacbcSBram Moolenaar        let s:sql_file_table     = ""
46334feacbcSBram Moolenaar        let s:sql_file_procedure = ""
46434feacbcSBram Moolenaar        let s:sql_file_view      = ""
465f193fffdSBram Moolenaar
466f193fffdSBram Moolenaar        let msg = "All SQL cached items have been removed."
467f193fffdSBram Moolenaar        call s:SQLCWarningMsg(msg)
468f193fffdSBram Moolenaar        " Leave time for the user to read the error message
469f193fffdSBram Moolenaar        :sleep 2
470e2f98b95SBram Moolenaar    else
471910f66f9SBram Moolenaar        let compl_list = s:SQLCGetSyntaxList(compl_type)
472e2f98b95SBram Moolenaar    endif
473e2f98b95SBram Moolenaar
474e2f98b95SBram Moolenaar    if base != ''
4755c73622aSBram Moolenaar        " Filter the list based on the first few characters the user entered.
4765c73622aSBram Moolenaar        " Check if the text matches at the beginning
477db7207e6SBram Moolenaar        "         \\(^.base.'\\)
4785c73622aSBram Moolenaar        " or
4795c73622aSBram Moolenaar        " Match to a owner.table or alias.column type match
480db7207e6SBram Moolenaar        "         ^\\(\\w\\+\\.\\)\\?'.base.'\\)
4815c73622aSBram Moolenaar        " or
4825c73622aSBram Moolenaar        " Handle names with spaces "my table name"
483db7207e6SBram Moolenaar        "         "\\(^'.base.'\\|^\\(\\w\\+\\.\\)\\?'.base.'\\)"'
484db7207e6SBram Moolenaar        "
4855c73622aSBram Moolenaar        let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|^\\(\\w\\+\\.\\)\\?'.base.'\\)"'
4865c73622aSBram Moolenaar        " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\)"'
4875c73622aSBram Moolenaar        " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|\\(\\.\\)\\?'.base.'\\)"'
4885c73622aSBram Moolenaar        " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|\\([^.]*\\)\\?'.base.'\\)"'
489eb3593b3SBram Moolenaar        let compl_list = filter(deepcopy(compl_list), expr)
49034feacbcSBram Moolenaar
49134feacbcSBram Moolenaar        if empty(compl_list) && compl_type == 'table' && base =~ '\.$'
49234feacbcSBram Moolenaar            " It is possible we could be looking for column name completion
49334feacbcSBram Moolenaar            " and the user simply hit C-X C-O to lets try it as well
49434feacbcSBram Moolenaar            " since we had no hits with the tables.
49534feacbcSBram Moolenaar            " If the base ends with a . it is hard to know if we are
49634feacbcSBram Moolenaar            " completing table names or column names.
49734feacbcSBram Moolenaar            let list_type = ''
49834feacbcSBram Moolenaar
49934feacbcSBram Moolenaar            let compl_list  = s:SQLCGetColumns(base, list_type)
50034feacbcSBram Moolenaar        endif
501e2f98b95SBram Moolenaar    endif
502e2f98b95SBram Moolenaar
503910f66f9SBram Moolenaar    if exists('b:sql_compl_savefunc') && b:sql_compl_savefunc != ""
504910f66f9SBram Moolenaar        let &omnifunc = b:sql_compl_savefunc
505910f66f9SBram Moolenaar    endif
506910f66f9SBram Moolenaar
50734feacbcSBram Moolenaar    if empty(compl_list)
50834feacbcSBram Moolenaar        call s:SQLCWarningMsg( 'Could not find type['.compl_type.'] using prepend[.'.s:prepended.'] base['.a:base.']' )
50934feacbcSBram Moolenaar    endif
51034feacbcSBram Moolenaar
511e2f98b95SBram Moolenaar    return compl_list
512e2f98b95SBram Moolenaarendfunc
513e2f98b95SBram Moolenaar
514910f66f9SBram Moolenaarfunction! sqlcomplete#PreCacheSyntax(...)
515910f66f9SBram Moolenaar    let syn_group_arr = []
516f9d5ca1dSBram Moolenaar    let syn_items     = []
517f9d5ca1dSBram Moolenaar
518910f66f9SBram Moolenaar    if a:0 > 0
519f9d5ca1dSBram Moolenaar        if type(a:1) != 3
520f9d5ca1dSBram Moolenaar            call s:SQLCWarningMsg("Parameter is not a list. Example:['syntaxGroup1', 'syntaxGroup2']")
521f9d5ca1dSBram Moolenaar            return ''
522f9d5ca1dSBram Moolenaar        endif
523910f66f9SBram Moolenaar        let syn_group_arr = a:1
524910f66f9SBram Moolenaar    else
525910f66f9SBram Moolenaar        let syn_group_arr = g:omni_sql_precache_syntax_groups
526910f66f9SBram Moolenaar    endif
52783e138c6SBram Moolenaar    " For each group specified in the list, precache all
52883e138c6SBram Moolenaar    " the sytnax items.
529910f66f9SBram Moolenaar    if !empty(syn_group_arr)
530910f66f9SBram Moolenaar        for group_name in syn_group_arr
531f9d5ca1dSBram Moolenaar            let syn_items = extend( syn_items, s:SQLCGetSyntaxList(group_name) )
532f9d5ca1dSBram Moolenaar        endfor
533f9d5ca1dSBram Moolenaar    endif
534f9d5ca1dSBram Moolenaar
535f9d5ca1dSBram Moolenaar    return syn_items
536f9d5ca1dSBram Moolenaarendfunction
537f9d5ca1dSBram Moolenaar
538f9d5ca1dSBram Moolenaarfunction! sqlcomplete#ResetCacheSyntax(...)
539f9d5ca1dSBram Moolenaar    let syn_group_arr = []
540f9d5ca1dSBram Moolenaar
541f9d5ca1dSBram Moolenaar    if a:0 > 0
542f9d5ca1dSBram Moolenaar        if type(a:1) != 3
543f9d5ca1dSBram Moolenaar            call s:SQLCWarningMsg("Parameter is not a list. Example:['syntaxGroup1', 'syntaxGroup2']")
544f9d5ca1dSBram Moolenaar            return ''
545f9d5ca1dSBram Moolenaar        endif
546f9d5ca1dSBram Moolenaar        let syn_group_arr = a:1
547f9d5ca1dSBram Moolenaar    else
548f9d5ca1dSBram Moolenaar        let syn_group_arr = g:omni_sql_precache_syntax_groups
549f9d5ca1dSBram Moolenaar    endif
550f9d5ca1dSBram Moolenaar    " For each group specified in the list, precache all
551f9d5ca1dSBram Moolenaar    " the sytnax items.
552f9d5ca1dSBram Moolenaar    if !empty(syn_group_arr)
553f9d5ca1dSBram Moolenaar        for group_name in syn_group_arr
554f9d5ca1dSBram Moolenaar            let list_idx = index(s:syn_list, group_name, 0, &ignorecase)
555f9d5ca1dSBram Moolenaar            if list_idx > -1
556f9d5ca1dSBram Moolenaar                " Remove from list of groups
557f9d5ca1dSBram Moolenaar                call remove( s:syn_list, list_idx )
558f9d5ca1dSBram Moolenaar                " Remove from list of keywords
559f9d5ca1dSBram Moolenaar                call remove( s:syn_value, list_idx )
560f9d5ca1dSBram Moolenaar            endif
561910f66f9SBram Moolenaar        endfor
562910f66f9SBram Moolenaar    endif
563910f66f9SBram Moolenaarendfunction
564910f66f9SBram Moolenaar
565910f66f9SBram Moolenaarfunction! sqlcomplete#Map(type)
566910f66f9SBram Moolenaar    " Tell the SQL plugin what you want to complete
567910f66f9SBram Moolenaar    let b:sql_compl_type=a:type
568910f66f9SBram Moolenaar    " Record previous omnifunc, if the SQL completion
569910f66f9SBram Moolenaar    " is being used in conjunction with other filetype
570910f66f9SBram Moolenaar    " completion plugins
571910f66f9SBram Moolenaar    if &omnifunc != "" && &omnifunc != 'sqlcomplete#Complete'
572910f66f9SBram Moolenaar        " Record the previous omnifunc, the plugin
573910f66f9SBram Moolenaar        " will automatically set this back so that it
574910f66f9SBram Moolenaar        " does not interfere with other ftplugins settings
575910f66f9SBram Moolenaar        let b:sql_compl_savefunc=&omnifunc
576910f66f9SBram Moolenaar    endif
577910f66f9SBram Moolenaar    " Set the OMNI func for the SQL completion plugin
578910f66f9SBram Moolenaar    let &omnifunc='sqlcomplete#Complete'
579910f66f9SBram Moolenaarendfunction
580910f66f9SBram Moolenaar
581f193fffdSBram Moolenaarfunction! sqlcomplete#DrillIntoTable()
582f193fffdSBram Moolenaar    " If the omni popup window is visible
583f193fffdSBram Moolenaar    if pumvisible()
584f193fffdSBram Moolenaar        call sqlcomplete#Map('column')
585f193fffdSBram Moolenaar        " C-Y, makes the currently highlighted entry active
586f193fffdSBram Moolenaar        " and trigger the omni popup to be redisplayed
58700a927d6SBram Moolenaar        call feedkeys("\<C-Y>\<C-X>\<C-O>", 'n')
588f193fffdSBram Moolenaar    else
589f193fffdSBram Moolenaar	" If the popup is not visible, simple perform the normal
59000a927d6SBram Moolenaar	" key behaviour.
59100a927d6SBram Moolenaar	" Must use exec since they key must be preceeded by "\"
59200a927d6SBram Moolenaar	" or feedkeys will simply push each character of the string
59300a927d6SBram Moolenaar	" rather than the "key press".
59400a927d6SBram Moolenaar        exec 'call feedkeys("\'.g:ftplugin_sql_omni_key_right.'", "n")'
595f193fffdSBram Moolenaar    endif
596f193fffdSBram Moolenaar    return ""
597f193fffdSBram Moolenaarendfunction
598f193fffdSBram Moolenaar
599f193fffdSBram Moolenaarfunction! sqlcomplete#DrillOutOfColumns()
600f193fffdSBram Moolenaar    " If the omni popup window is visible
601f193fffdSBram Moolenaar    if pumvisible()
602f193fffdSBram Moolenaar        call sqlcomplete#Map('tableReset')
603f193fffdSBram Moolenaar        " Trigger the omni popup to be redisplayed
604f193fffdSBram Moolenaar        call feedkeys("\<C-X>\<C-O>")
605f193fffdSBram Moolenaar    else
606f193fffdSBram Moolenaar	" If the popup is not visible, simple perform the normal
60700a927d6SBram Moolenaar	" key behaviour.
60800a927d6SBram Moolenaar	" Must use exec since they key must be preceeded by "\"
60900a927d6SBram Moolenaar	" or feedkeys will simply push each character of the string
61000a927d6SBram Moolenaar	" rather than the "key press".
61100a927d6SBram Moolenaar        exec 'call feedkeys("\'.g:ftplugin_sql_omni_key_left.'", "n")'
612f193fffdSBram Moolenaar    endif
613f193fffdSBram Moolenaar    return ""
614f193fffdSBram Moolenaarendfunction
615f193fffdSBram Moolenaar
616f193fffdSBram Moolenaarfunction! s:SQLCWarningMsg(msg)
617f193fffdSBram Moolenaar    echohl WarningMsg
618f9d5ca1dSBram Moolenaar    echomsg 'SQLComplete:'.a:msg
619f193fffdSBram Moolenaar    echohl None
620f193fffdSBram Moolenaarendfunction
621f193fffdSBram Moolenaar
622f193fffdSBram Moolenaarfunction! s:SQLCErrorMsg(msg)
623f193fffdSBram Moolenaar    echohl ErrorMsg
624f9d5ca1dSBram Moolenaar    echomsg 'SQLComplete:'.a:msg
625f193fffdSBram Moolenaar    echohl None
626f193fffdSBram Moolenaarendfunction
627f193fffdSBram Moolenaar
628910f66f9SBram Moolenaarfunction! s:SQLCGetSyntaxList(syn_group)
629910f66f9SBram Moolenaar    let syn_group  = a:syn_group
630910f66f9SBram Moolenaar    let compl_list = []
631910f66f9SBram Moolenaar
632910f66f9SBram Moolenaar    " Check if we have already cached the syntax list
633910f66f9SBram Moolenaar    let list_idx = index(s:syn_list, syn_group, 0, &ignorecase)
634910f66f9SBram Moolenaar    if list_idx > -1
635910f66f9SBram Moolenaar        " Return previously cached value
636910f66f9SBram Moolenaar        let compl_list = s:syn_value[list_idx]
637910f66f9SBram Moolenaar    else
638*ad3b366cSBram Moolenaar        let s:save_inc = g:omni_syntax_group_include_sql
639*ad3b366cSBram Moolenaar        let s:save_exc = g:omni_syntax_group_exclude_sql
640*ad3b366cSBram Moolenaar        let g:omni_syntax_group_include_sql = ''
641*ad3b366cSBram Moolenaar        let g:omni_syntax_group_exclude_sql = ''
642*ad3b366cSBram Moolenaar
643910f66f9SBram Moolenaar        " Request the syntax list items from the
644910f66f9SBram Moolenaar        " syntax completion plugin
645910f66f9SBram Moolenaar        if syn_group == 'syntax'
646910f66f9SBram Moolenaar            " Handle this special case.  This allows the user
647910f66f9SBram Moolenaar            " to indicate they want all the syntax items available,
648910f66f9SBram Moolenaar            " so do not specify a specific include list.
649*ad3b366cSBram Moolenaar            let syn_value                       = syntaxcomplete#OmniSyntaxList()
650910f66f9SBram Moolenaar        else
651910f66f9SBram Moolenaar            " The user has specified a specific syntax group
652910f66f9SBram Moolenaar            let g:omni_syntax_group_include_sql = syn_group
653*ad3b366cSBram Moolenaar            let syn_value                       = syntaxcomplete#OmniSyntaxList(syn_group)
654910f66f9SBram Moolenaar        endif
655910f66f9SBram Moolenaar        let g:omni_syntax_group_include_sql = s:save_inc
656910f66f9SBram Moolenaar        let g:omni_syntax_group_exclude_sql = s:save_exc
657910f66f9SBram Moolenaar        " Cache these values for later use
658910f66f9SBram Moolenaar        let s:syn_list  = add( s:syn_list,  syn_group )
659910f66f9SBram Moolenaar        let s:syn_value = add( s:syn_value, syn_value )
660910f66f9SBram Moolenaar        let compl_list  = syn_value
661910f66f9SBram Moolenaar    endif
662910f66f9SBram Moolenaar
663910f66f9SBram Moolenaar    return compl_list
664910f66f9SBram Moolenaarendfunction
665910f66f9SBram Moolenaar
666e2f98b95SBram Moolenaarfunction! s:SQLCCheck4dbext()
667e2f98b95SBram Moolenaar    if !exists('g:loaded_dbext')
668e2f98b95SBram Moolenaar        let msg = "The dbext plugin must be loaded for dynamic SQL completion"
669e2f98b95SBram Moolenaar        call s:SQLCErrorMsg(msg)
670e2f98b95SBram Moolenaar        " Leave time for the user to read the error message
671e2f98b95SBram Moolenaar        :sleep 2
672e2f98b95SBram Moolenaar        return -1
6738c8de839SBram Moolenaar    elseif g:loaded_dbext < 600
6748c8de839SBram Moolenaar        let msg = "The dbext plugin must be at least version 5.30 " .
675e2f98b95SBram Moolenaar                    \ " for dynamic SQL completion"
676e2f98b95SBram Moolenaar        call s:SQLCErrorMsg(msg)
677e2f98b95SBram Moolenaar        " Leave time for the user to read the error message
678e2f98b95SBram Moolenaar        :sleep 2
679e2f98b95SBram Moolenaar        return -1
680e2f98b95SBram Moolenaar    endif
681e2f98b95SBram Moolenaar    return 1
682e2f98b95SBram Moolenaarendfunction
683e2f98b95SBram Moolenaar
684e2f98b95SBram Moolenaarfunction! s:SQLCAddAlias(table_name, table_alias, cols)
685f193fffdSBram Moolenaar    " Strip off the owner if included
686f193fffdSBram Moolenaar    let table_name  = matchstr(a:table_name, '\%(.\{-}\.\)\?\zs\(.*\)' )
687e2f98b95SBram Moolenaar    let table_alias = a:table_alias
688e2f98b95SBram Moolenaar    let cols        = a:cols
689e2f98b95SBram Moolenaar
690e2f98b95SBram Moolenaar    if g:omni_sql_use_tbl_alias != 'n'
691e2f98b95SBram Moolenaar        if table_alias == ''
692e2f98b95SBram Moolenaar            if 'da' =~? g:omni_sql_use_tbl_alias
693e2f98b95SBram Moolenaar                if table_name =~ '_'
694e2f98b95SBram Moolenaar                    " Treat _ as separators since people often use these
695e2f98b95SBram Moolenaar                    " for word separators
696e2f98b95SBram Moolenaar                    let save_keyword = &iskeyword
697e2f98b95SBram Moolenaar                    setlocal iskeyword-=_
698e2f98b95SBram Moolenaar
699e2f98b95SBram Moolenaar                    " Get the first letter of each word
700e2f98b95SBram Moolenaar                    " [[:alpha:]] is used instead of \w
701e2f98b95SBram Moolenaar                    " to catch extended accented characters
702e2f98b95SBram Moolenaar                    "
703e2f98b95SBram Moolenaar                    let table_alias = substitute(
704e2f98b95SBram Moolenaar                                \ table_name,
705e2f98b95SBram Moolenaar                                \ '\<[[:alpha:]]\+\>_\?',
706e2f98b95SBram Moolenaar                                \ '\=strpart(submatch(0), 0, 1)',
707e2f98b95SBram Moolenaar                                \ 'g'
708e2f98b95SBram Moolenaar                                \ )
709e2f98b95SBram Moolenaar                    " Restore original value
710e2f98b95SBram Moolenaar                    let &iskeyword = save_keyword
711e2f98b95SBram Moolenaar                elseif table_name =~ '\u\U'
712f193fffdSBram Moolenaar                    let table_alias = substitute(
713e2f98b95SBram Moolenaar                                \ table_name, '\(\u\)\U*', '\1', 'g')
714e2f98b95SBram Moolenaar                else
715e2f98b95SBram Moolenaar                    let table_alias = strpart(table_name, 0, 1)
716e2f98b95SBram Moolenaar                endif
717e2f98b95SBram Moolenaar            endif
718e2f98b95SBram Moolenaar        endif
719e2f98b95SBram Moolenaar        if table_alias != ''
720e2f98b95SBram Moolenaar            " Following a word character, make sure there is a . and no spaces
721e2f98b95SBram Moolenaar            let table_alias = substitute(table_alias, '\w\zs\.\?\s*$', '.', '')
722e2f98b95SBram Moolenaar            if 'a' =~? g:omni_sql_use_tbl_alias && a:table_alias == ''
723e2f98b95SBram Moolenaar                let table_alias = inputdialog("Enter table alias:", table_alias)
724e2f98b95SBram Moolenaar            endif
725e2f98b95SBram Moolenaar        endif
726e2f98b95SBram Moolenaar        if table_alias != ''
727e2f98b95SBram Moolenaar            let cols = substitute(cols, '\<\w', table_alias.'&', 'g')
728e2f98b95SBram Moolenaar        endif
729e2f98b95SBram Moolenaar    endif
730e2f98b95SBram Moolenaar
731e2f98b95SBram Moolenaar    return cols
732e2f98b95SBram Moolenaarendfunction
733e2f98b95SBram Moolenaar
73483e138c6SBram Moolenaarfunction! s:SQLCGetObjectOwner(object)
73583e138c6SBram Moolenaar    " The owner regex matches a word at the start of the string which is
73683e138c6SBram Moolenaar    " followed by a dot, but doesn't include the dot in the result.
73783e138c6SBram Moolenaar    " ^           - from beginning of line
7388c8de839SBram Moolenaar    " \("\|\[\)\? - ignore any quotes
73983e138c6SBram Moolenaar    " \zs         - start the match now
7408c8de839SBram Moolenaar    " .\{-}       - get owner name
74183e138c6SBram Moolenaar    " \ze         - end the match
7428c8de839SBram Moolenaar    " \("\|\[\)\? - ignore any quotes
74383e138c6SBram Moolenaar    " \.          - must by followed by a .
7448c8de839SBram Moolenaar    " let owner = matchstr( a:object, '^\s*\zs.*\ze\.' )
7458c8de839SBram Moolenaar    let owner = matchstr( a:object, '^\("\|\[\)\?\zs\.\{-}\ze\("\|\]\)\?\.' )
74683e138c6SBram Moolenaar    return owner
74783e138c6SBram Moolenaarendfunction
74883e138c6SBram Moolenaar
749e2f98b95SBram Moolenaarfunction! s:SQLCGetColumns(table_name, list_type)
75034feacbcSBram Moolenaar    if a:table_name =~ '\.'
75134feacbcSBram Moolenaar        " Check if the owner/creator has been specified
75234feacbcSBram Moolenaar        let owner  = matchstr( a:table_name, '^\zs.*\ze\..*\..*' )
75334feacbcSBram Moolenaar        let table  = matchstr( a:table_name, '^\(.*\.\)\?\zs.*\ze\..*' )
75434feacbcSBram Moolenaar        let column = matchstr( a:table_name, '.*\.\zs.*' )
75534feacbcSBram Moolenaar
75634feacbcSBram Moolenaar        if g:omni_sql_include_owner == 1 && owner == '' && table != '' && column != ''
75734feacbcSBram Moolenaar            let owner  = table
75834feacbcSBram Moolenaar            let table  = column
75934feacbcSBram Moolenaar            let column = ''
76034feacbcSBram Moolenaar        endif
76134feacbcSBram Moolenaar    else
76234feacbcSBram Moolenaar        let owner  = ''
76334feacbcSBram Moolenaar        let table  = matchstr(a:table_name, '^["\[\]a-zA-Z0-9_ ]\+\ze\.\?')
76434feacbcSBram Moolenaar        let column = ''
76534feacbcSBram Moolenaar    endif
76634feacbcSBram Moolenaar
76783e138c6SBram Moolenaar    " Check if the table name was provided as part of the column name
76834feacbcSBram Moolenaar    " let table_name   = matchstr(a:table_name, '^["\[\]a-zA-Z0-9_ ]\+\ze\.\?')
76934feacbcSBram Moolenaar    let table_name   = table
770e2f98b95SBram Moolenaar    let table_cols   = []
771e2f98b95SBram Moolenaar    let table_alias  = ''
772e2f98b95SBram Moolenaar    let move_to_top  = 1
773e2f98b95SBram Moolenaar
7748c8de839SBram Moolenaar    let table_name   = substitute(table_name, '\s*\(.\{-}\)\s*$', '\1', 'g')
7758c8de839SBram Moolenaar
7768c8de839SBram Moolenaar    " If the table name was given as:
7778c8de839SBram Moolenaar    "     where c.
7788c8de839SBram Moolenaar    let table_name   = substitute(table_name, '^\c\(WHERE\|AND\|OR\)\s\+', '', '')
779eb3593b3SBram Moolenaar    if g:loaded_dbext >= 300
780e2f98b95SBram Moolenaar        let saveSettingAlias = DB_listOption('use_tbl_alias')
781e2f98b95SBram Moolenaar        exec 'DBSetOption use_tbl_alias=n'
782e2f98b95SBram Moolenaar    endif
783e2f98b95SBram Moolenaar
7848c8de839SBram Moolenaar    let table_name_stripped = substitute(table_name, '["\[\]]*', '', 'g')
7858c8de839SBram Moolenaar
786e2f98b95SBram Moolenaar    " Check if we have already cached the column list for this table
787e2f98b95SBram Moolenaar    " by its name
7888c8de839SBram Moolenaar    let list_idx = index(s:tbl_name, table_name_stripped, 0, &ignorecase)
789e2f98b95SBram Moolenaar    if list_idx > -1
7908c8de839SBram Moolenaar        let table_cols = split(s:tbl_cols[list_idx], '\n')
791e2f98b95SBram Moolenaar    else
792e2f98b95SBram Moolenaar        " Check if we have already cached the column list for this table
793e2f98b95SBram Moolenaar        " by its alias, assuming the table_name provided was actually
794e2f98b95SBram Moolenaar        " the alias for the table instead
795e2f98b95SBram Moolenaar        "     select *
796e2f98b95SBram Moolenaar        "       from area a
797e2f98b95SBram Moolenaar        "      where a.
7988c8de839SBram Moolenaar        let list_idx = index(s:tbl_alias, table_name_stripped, 0, &ignorecase)
799e2f98b95SBram Moolenaar        if list_idx > -1
8008c8de839SBram Moolenaar            let table_alias = table_name_stripped
801e2f98b95SBram Moolenaar            let table_name  = s:tbl_name[list_idx]
8028c8de839SBram Moolenaar            let table_cols  = split(s:tbl_cols[list_idx], '\n')
803e2f98b95SBram Moolenaar        endif
804e2f98b95SBram Moolenaar    endif
805e2f98b95SBram Moolenaar
806e2f98b95SBram Moolenaar    " If we have not found a cached copy of the table
807e2f98b95SBram Moolenaar    " And the table ends in a "." or we are looking for a column list
808e2f98b95SBram Moolenaar    " if list_idx == -1 && (a:table_name =~ '\.' || b:sql_compl_type =~ 'column')
809e2f98b95SBram Moolenaar    " if list_idx == -1 && (a:table_name =~ '\.' || a:list_type =~ 'csv')
810e2f98b95SBram Moolenaar    if list_idx == -1
811e2f98b95SBram Moolenaar         let saveY      = @y
812e2f98b95SBram Moolenaar         let saveSearch = @/
813e2f98b95SBram Moolenaar         let saveWScan  = &wrapscan
814e2f98b95SBram Moolenaar         let curline    = line(".")
815e2f98b95SBram Moolenaar         let curcol     = col(".")
816e2f98b95SBram Moolenaar
817e2f98b95SBram Moolenaar         " Do not let searchs wrap
818e2f98b95SBram Moolenaar         setlocal nowrapscan
819e2f98b95SBram Moolenaar         " If . was entered, look at the word just before the .
820e2f98b95SBram Moolenaar         " We are looking for something like this:
821e2f98b95SBram Moolenaar         "    select *
822e2f98b95SBram Moolenaar         "      from customer c
823e2f98b95SBram Moolenaar         "     where c.
824e2f98b95SBram Moolenaar         " So when . is pressed, we need to find 'c'
825e2f98b95SBram Moolenaar         "
826e2f98b95SBram Moolenaar
827e2f98b95SBram Moolenaar         " Search backwards to the beginning of the statement
828e2f98b95SBram Moolenaar         " and do NOT wrap
829e2f98b95SBram Moolenaar         " exec 'silent! normal! v?\<\(select\|update\|delete\|;\)\>'."\n".'"yy'
83000a927d6SBram Moolenaar         exec 'silent! normal! ?\<\c\(select\|update\|delete\|;\)\>'."\n"
831e2f98b95SBram Moolenaar
832e2f98b95SBram Moolenaar         " Start characterwise visual mode
833e2f98b95SBram Moolenaar         " Advance right one character
834e2f98b95SBram Moolenaar         " Search foward until one of the following:
835e2f98b95SBram Moolenaar         "     1.  Another select/update/delete statement
836e2f98b95SBram Moolenaar         "     2.  A ; at the end of a line (the delimiter)
837e2f98b95SBram Moolenaar         "     3.  The end of the file (incase no delimiter)
838e2f98b95SBram Moolenaar         " Yank the visually selected text into the "y register.
83900a927d6SBram Moolenaar         exec 'silent! normal! vl/\c\(\<select\>\|\<update\>\|\<delete\>\|;\s*$\|\%$\)'."\n".'"yy'
840e2f98b95SBram Moolenaar
841e2f98b95SBram Moolenaar         let query = @y
842e2f98b95SBram Moolenaar         let query = substitute(query, "\n", ' ', 'g')
843e2f98b95SBram Moolenaar         let found = 0
844e2f98b95SBram Moolenaar
84500a927d6SBram Moolenaar         " if query =~? '^\c\(select\)'
84600a927d6SBram Moolenaar         if query =~? '^\(select\|update\|delete\)'
847e2f98b95SBram Moolenaar             let found = 1
848e2f98b95SBram Moolenaar             "  \(\(\<\w\+\>\)\.\)\?   -
84900a927d6SBram Moolenaar             " '\c\(from\|join\|,\).\{-}'  - Starting at the from clause (case insensitive)
850e2f98b95SBram Moolenaar             " '\zs\(\(\<\w\+\>\)\.\)\?' - Get the owner name (optional)
851e2f98b95SBram Moolenaar             " '\<\w\+\>\ze' - Get the table name
852e2f98b95SBram Moolenaar             " '\s\+\<'.table_name.'\>' - Followed by the alias
853e2f98b95SBram Moolenaar             " '\s*\.\@!.*'  - Cannot be followed by a .
854e2f98b95SBram Moolenaar             " '\(\<where\>\|$\)' - Must be followed by a WHERE clause
855e2f98b95SBram Moolenaar             " '.*'  - Exclude the rest of the line in the match
85600a927d6SBram Moolenaar             " let table_name_new = matchstr(@y,
85700a927d6SBram Moolenaar             "             \ '\c\(from\|join\|,\).\{-}'.
85800a927d6SBram Moolenaar             "             \ '\zs\(\("\|\[\)\?.\{-}\("\|\]\)\.\)\?'.
85900a927d6SBram Moolenaar             "             \ '\("\|\[\)\?.\{-}\("\|\]\)\?\ze'.
86000a927d6SBram Moolenaar             "             \ '\s\+\%(as\s\+\)\?\<'.
86100a927d6SBram Moolenaar             "             \ matchstr(table_name, '.\{-}\ze\.\?$').
86200a927d6SBram Moolenaar             "             \ '\>'.
86300a927d6SBram Moolenaar             "             \ '\s*\.\@!.*'.
86400a927d6SBram Moolenaar             "             \ '\(\<where\>\|$\)'.
86500a927d6SBram Moolenaar             "             \ '.*'
86600a927d6SBram Moolenaar             "             \ )
867db7207e6SBram Moolenaar             "
868db7207e6SBram Moolenaar             "
869db7207e6SBram Moolenaar             " ''\c\(\<from\>\|\<join\>\|,\)\s*'  - Starting at the from clause (case insensitive)
870db7207e6SBram Moolenaar             " '\zs\(\("\|\[\)\?\w\+\("\|\]\)\?\.\)\?' - Get the owner name (optional)
871db7207e6SBram Moolenaar             " '\("\|\[\)\?\w\+\("\|\]\)\?\ze' - Get the table name
872db7207e6SBram Moolenaar             " '\s\+\%(as\s\+\)\?\<'.matchstr(table_name, '.\{-}\ze\.\?$').'\>' - Followed by the alias
873db7207e6SBram Moolenaar             " '\s*\.\@!.*'  - Cannot be followed by a .
874db7207e6SBram Moolenaar             " '\(\<where\>\|$\)' - Must be followed by a WHERE clause
875db7207e6SBram Moolenaar             " '.*'  - Exclude the rest of the line in the match
876e2f98b95SBram Moolenaar             let table_name_new = matchstr(@y,
87700a927d6SBram Moolenaar                         \ '\c\(\<from\>\|\<join\>\|,\)\s*'.
87800a927d6SBram Moolenaar                         \ '\zs\(\("\|\[\)\?\w\+\("\|\]\)\?\.\)\?'.
87900a927d6SBram Moolenaar                         \ '\("\|\[\)\?\w\+\("\|\]\)\?\ze'.
880f193fffdSBram Moolenaar                         \ '\s\+\%(as\s\+\)\?\<'.
881f193fffdSBram Moolenaar                         \ matchstr(table_name, '.\{-}\ze\.\?$').
882f193fffdSBram Moolenaar                         \ '\>'.
883e2f98b95SBram Moolenaar                         \ '\s*\.\@!.*'.
884e2f98b95SBram Moolenaar                         \ '\(\<where\>\|$\)'.
885e2f98b95SBram Moolenaar                         \ '.*'
886e2f98b95SBram Moolenaar                         \ )
8878c8de839SBram Moolenaar
888e2f98b95SBram Moolenaar             if table_name_new != ''
889e2f98b95SBram Moolenaar                 let table_alias = table_name
89034feacbcSBram Moolenaar                 if g:omni_sql_include_owner == 1
89134feacbcSBram Moolenaar                    let table_name  = matchstr( table_name_new, '^\zs\(.\{-}\.\)\?\(.\{-}\.\)\?.*\ze' )
89234feacbcSBram Moolenaar                 else
89334feacbcSBram Moolenaar                     " let table_name  = matchstr( table_name_new, '^\(.*\.\)\?\zs.*\ze' )
89434feacbcSBram Moolenaar                    let table_name  = matchstr( table_name_new, '^\(.\{-}\.\)\?\zs\(.\{-}\.\)\?.*\ze' )
89534feacbcSBram Moolenaar                 endif
896e2f98b95SBram Moolenaar
897e2f98b95SBram Moolenaar                 let list_idx = index(s:tbl_name, table_name, 0, &ignorecase)
898e2f98b95SBram Moolenaar                 if list_idx > -1
899e2f98b95SBram Moolenaar                     let table_cols  = split(s:tbl_cols[list_idx])
900e2f98b95SBram Moolenaar                     let s:tbl_name[list_idx]  = table_name
901e2f98b95SBram Moolenaar                     let s:tbl_alias[list_idx] = table_alias
902e2f98b95SBram Moolenaar                 else
903e2f98b95SBram Moolenaar                     let list_idx = index(s:tbl_alias, table_name, 0, &ignorecase)
904e2f98b95SBram Moolenaar                     if list_idx > -1
905e2f98b95SBram Moolenaar                         let table_cols = split(s:tbl_cols[list_idx])
906e2f98b95SBram Moolenaar                         let s:tbl_name[list_idx]  = table_name
907e2f98b95SBram Moolenaar                         let s:tbl_alias[list_idx] = table_alias
908e2f98b95SBram Moolenaar                     endif
909e2f98b95SBram Moolenaar                 endif
910e2f98b95SBram Moolenaar
911e2f98b95SBram Moolenaar             endif
912e2f98b95SBram Moolenaar         else
913e2f98b95SBram Moolenaar             " Simply assume it is a table name provided with a . on the end
914e2f98b95SBram Moolenaar             let found = 1
915e2f98b95SBram Moolenaar         endif
916e2f98b95SBram Moolenaar
917e2f98b95SBram Moolenaar         let @y        = saveY
918e2f98b95SBram Moolenaar         let @/        = saveSearch
919e2f98b95SBram Moolenaar         let &wrapscan = saveWScan
920e2f98b95SBram Moolenaar
921e2f98b95SBram Moolenaar         " Return to previous location
922e2f98b95SBram Moolenaar         call cursor(curline, curcol)
923e2f98b95SBram Moolenaar
924e2f98b95SBram Moolenaar         if found == 0
925eb3593b3SBram Moolenaar             if g:loaded_dbext > 300
926e2f98b95SBram Moolenaar                 exec 'DBSetOption use_tbl_alias='.saveSettingAlias
927e2f98b95SBram Moolenaar             endif
928e2f98b95SBram Moolenaar
929e2f98b95SBram Moolenaar             " Not a SQL statement, do not display a list
930e2f98b95SBram Moolenaar             return []
931e2f98b95SBram Moolenaar         endif
932e2f98b95SBram Moolenaar    endif
933e2f98b95SBram Moolenaar
934e2f98b95SBram Moolenaar    if empty(table_cols)
935e2f98b95SBram Moolenaar        " Specify silent mode, no messages to the user (tbl, 1)
936e2f98b95SBram Moolenaar        " Specify do not comma separate (tbl, 1, 1)
93734feacbcSBram Moolenaar        " let table_cols_str = DB_getListColumn(table_name, 1, 1)
93834feacbcSBram Moolenaar        let table_cols_str = DB_getListColumn((owner!=''?owner.'.':'').table_name, 1, 1)
939e2f98b95SBram Moolenaar
940e2f98b95SBram Moolenaar        if table_cols_str != ""
941e2f98b95SBram Moolenaar            let s:tbl_name  = add( s:tbl_name,  table_name )
942e2f98b95SBram Moolenaar            let s:tbl_alias = add( s:tbl_alias, table_alias )
943e2f98b95SBram Moolenaar            let s:tbl_cols  = add( s:tbl_cols,  table_cols_str )
9448c8de839SBram Moolenaar            let table_cols  = split(table_cols_str, '\n')
945e2f98b95SBram Moolenaar        endif
946e2f98b95SBram Moolenaar
947e2f98b95SBram Moolenaar    endif
948e2f98b95SBram Moolenaar
949eb3593b3SBram Moolenaar    if g:loaded_dbext > 300
950e2f98b95SBram Moolenaar        exec 'DBSetOption use_tbl_alias='.saveSettingAlias
951e2f98b95SBram Moolenaar    endif
952e2f98b95SBram Moolenaar
953f193fffdSBram Moolenaar    " If the user has asked for a comma separate list of column
954f193fffdSBram Moolenaar    " values, ask the user if they want to prepend each column
955f193fffdSBram Moolenaar    " with a tablename alias.
956e2f98b95SBram Moolenaar    if a:list_type == 'csv' && !empty(table_cols)
957e2f98b95SBram Moolenaar        let cols       = join(table_cols, ', ')
958e2f98b95SBram Moolenaar        let cols       = s:SQLCAddAlias(table_name, table_alias, cols)
959e2f98b95SBram Moolenaar        let table_cols = [cols]
960e2f98b95SBram Moolenaar    endif
961e2f98b95SBram Moolenaar
962e2f98b95SBram Moolenaar    return table_cols
963e2f98b95SBram Moolenaarendfunction
96434feacbcSBram Moolenaar"  Restore:
96534feacbcSBram Moolenaarlet &cpo= s:keepcpo
96634feacbcSBram Moolenaarunlet s:keepcpo
96734feacbcSBram Moolenaar" vim: ts=4 fdm=marker
968