1f193fffdSBram Moolenaar" Vim OMNI completion script for SQL
2e2f98b95SBram Moolenaar" Language:    SQL
35c73622aSBram Moolenaar" Maintainer:  David Fishburn <dfishburn dot vim at gmail dot com>
45e9b2fa9SBram Moolenaar" Version:     16.0
501164a65SBram Moolenaar" Last Change: 2017 Oct 15
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"
14ad3b366cSBram Moolenaar" TODO
15ad3b366cSBram Moolenaar"     - Jonas Enberg - if no table is found when using column completion
16ad3b366cSBram Moolenaar"       look backwards to a FROM clause and find the first table
17ad3b366cSBram Moolenaar"       and complete it.
18ad3b366cSBram Moolenaar"
195e9b2fa9SBram Moolenaar" Version 16.0 (Dec 2015)
20*6c391a74SBram Moolenaar"     - NF: If resetting the cache and table, procedure or view completion
215e9b2fa9SBram Moolenaar"           had been used via dbext, have dbext delete or recreate the
225e9b2fa9SBram Moolenaar"           dictionary so that new objects are picked up for the
235e9b2fa9SBram Moolenaar"           next completion.
245e9b2fa9SBram Moolenaar"
25ad3b366cSBram Moolenaar" Version 15.0 (May 2013)
26ad3b366cSBram Moolenaar"     - NF: Changed the SQL precached syntax items, omni_sql_precache_syntax_groups,
27ad3b366cSBram Moolenaar"           to use regular expressions to pick up extended syntax group names.
28ad3b366cSBram Moolenaar"           This requires an updated SyntaxComplete plugin version 13.0.
29ad3b366cSBram Moolenaar"           If the required versions have not been installed, previous
30ad3b366cSBram Moolenaar"           behaviour will not be impacted.
31ad3b366cSBram Moolenaar"
3234feacbcSBram Moolenaar" Version 14.0 (Dec 2012)
3334feacbcSBram Moolenaar"     - BF: Added check for cpo
3434feacbcSBram Moolenaar"
3534feacbcSBram Moolenaar" Version 13.0 (Dec 2012)
3634feacbcSBram Moolenaar"     - NF: When completing column lists or drilling into a table
3734feacbcSBram Moolenaar"           and g:omni_sql_include_owner is enabled, the
3834feacbcSBram Moolenaar"           only the table name would be replaced with the column
3934feacbcSBram Moolenaar"           list instead of the table name and owner (if specified).
4034feacbcSBram Moolenaar"     - NF: When completing column lists using table aliases
4134feacbcSBram Moolenaar"           and g:omni_sql_include_owner is enabled, account
4234feacbcSBram Moolenaar"           for the owner name when looking up the table
4334feacbcSBram Moolenaar"           list instead of the table name and owner (if specified).
4434feacbcSBram Moolenaar"     - BF: When completing column lists or drilling into a table
4534feacbcSBram Moolenaar"           and g:omni_sql_include_owner is enabled, the
4634feacbcSBram Moolenaar"           column list could often not be found for the table.
4734feacbcSBram Moolenaar"     - BF: When OMNI popped up, possibly the wrong word
4834feacbcSBram Moolenaar"           would be replaced for column and column list options.
4934feacbcSBram Moolenaar"
5034feacbcSBram Moolenaar" Version 12.0 (Feb 2012)
51db7207e6SBram Moolenaar"     - Partial column name completion did not work when a table
52db7207e6SBram Moolenaar"       name or table alias was provided (Jonas Enberg).
53db7207e6SBram Moolenaar"     - Improved the handling of column completion.  First we match any
54db7207e6SBram Moolenaar"       columns from a previous completion.  If not matches are found, we
55db7207e6SBram Moolenaar"       consider the partial name to be a table or table alias for the
56db7207e6SBram Moolenaar"       query and attempt to match on it.
57db7207e6SBram Moolenaar"
5834feacbcSBram Moolenaar" Version 11.0 (Jan 2012)
59db7207e6SBram Moolenaar"     Added g:omni_sql_default_compl_type variable
60db7207e6SBram Moolenaar"         - You can specify which type of completion to default to
61db7207e6SBram Moolenaar"           when pressing <C-X><C-O>.  The entire list of available
62db7207e6SBram Moolenaar"           choices can be found in the calls to sqlcomplete#Map in:
63db7207e6SBram Moolenaar"               ftplugin/sql.vim
64db7207e6SBram Moolenaar"
65f9d5ca1dSBram Moolenaar" Version 10.0
66f9d5ca1dSBram Moolenaar"     Updated PreCacheSyntax()
67f9d5ca1dSBram Moolenaar"         - Now returns a List of the syntax items it finds.
68f9d5ca1dSBram Moolenaar"           This allows other plugins / scripts to use this list for their own
69f9d5ca1dSBram Moolenaar"           purposes.  In this case XPTemplate can use them for a Choose list.
70f9d5ca1dSBram Moolenaar"         - Verifies the parameters are the correct type and displays a
71f9d5ca1dSBram Moolenaar"           warning if not.
72f9d5ca1dSBram Moolenaar"         - Verifies the parameters are the correct type and displays a
73f9d5ca1dSBram Moolenaar"           warning if not.
74f9d5ca1dSBram Moolenaar"     Updated SQLCWarningMsg()
75f9d5ca1dSBram Moolenaar"         - Prepends warning message with SQLComplete so you know who issued
76f9d5ca1dSBram Moolenaar"           the warning.
77f9d5ca1dSBram Moolenaar"     Updated SQLCErrorMsg()
78f9d5ca1dSBram Moolenaar"         - Prepends error message with SQLComplete so you know who issued
79f9d5ca1dSBram Moolenaar"           the error.
80f9d5ca1dSBram Moolenaar"
8134feacbcSBram Moolenaar" Version 9.0 (May 2010)
8200a927d6SBram Moolenaar"     This change removes some of the support for tables with spaces in their
8300a927d6SBram Moolenaar"     names in order to simplify the regexes used to pull out query table
8400a927d6SBram Moolenaar"     aliases for more robust table name and column name code completion.
8500a927d6SBram Moolenaar"     Full support for "table names with spaces" can be added in again
8600a927d6SBram Moolenaar"     after 7.3.
87f9d5ca1dSBram Moolenaar"
8800a927d6SBram Moolenaar" Version 8.0
8900a927d6SBram Moolenaar"     Incorrectly re-executed the g:ftplugin_sql_omni_key_right and g:ftplugin_sql_omni_key_left
9000a927d6SBram Moolenaar"     when drilling in and out of a column list for a table.
91f9d5ca1dSBram Moolenaar"
9234feacbcSBram Moolenaar" Version 7.0 (Jan 2010)
935c73622aSBram Moolenaar"     Better handling of object names
94f9d5ca1dSBram Moolenaar"
9534feacbcSBram Moolenaar" Version 6.0 (Apr 2008)
965c73622aSBram Moolenaar"     Supports object names with spaces "my table name"
975c73622aSBram Moolenaar"
98e2f98b95SBram Moolenaar" Set completion with CTRL-X CTRL-O to autoloaded function.
99e2f98b95SBram Moolenaar" This check is in place in case this script is
100e2f98b95SBram Moolenaar" sourced directly instead of using the autoload feature.
101e2f98b95SBram Moolenaarif exists('&omnifunc')
102e2f98b95SBram Moolenaar    " Do not set the option if already set since this
103e2f98b95SBram Moolenaar    " results in an E117 warning.
104e2f98b95SBram Moolenaar    if &omnifunc == ""
105e2f98b95SBram Moolenaar        setlocal omnifunc=sqlcomplete#Complete
106e2f98b95SBram Moolenaar    endif
107e2f98b95SBram Moolenaarendif
108e2f98b95SBram Moolenaar
109e2f98b95SBram Moolenaarif exists('g:loaded_sql_completion')
110e2f98b95SBram Moolenaar    finish
111e2f98b95SBram Moolenaarendif
1125e9b2fa9SBram Moolenaarlet g:loaded_sql_completion = 160
11334feacbcSBram Moolenaarlet s:keepcpo= &cpo
11434feacbcSBram Moolenaarset cpo&vim
115e2f98b95SBram Moolenaar
116e2f98b95SBram Moolenaar" Maintains filename of dictionary
117e2f98b95SBram Moolenaarlet s:sql_file_table        = ""
118e2f98b95SBram Moolenaarlet s:sql_file_procedure    = ""
119e2f98b95SBram Moolenaarlet s:sql_file_view         = ""
120e2f98b95SBram Moolenaar
121e2f98b95SBram Moolenaar" Define various arrays to be used for caching
122e2f98b95SBram Moolenaarlet s:tbl_name              = []
123e2f98b95SBram Moolenaarlet s:tbl_alias             = []
124e2f98b95SBram Moolenaarlet s:tbl_cols              = []
125e2f98b95SBram Moolenaarlet s:syn_list              = []
126e2f98b95SBram Moolenaarlet s:syn_value             = []
127e2f98b95SBram Moolenaar
128e2f98b95SBram Moolenaar" Used in conjunction with the syntaxcomplete plugin
129e2f98b95SBram Moolenaarlet s:save_inc              = ""
130e2f98b95SBram Moolenaarlet s:save_exc              = ""
131ad3b366cSBram Moolenaarif !exists('g:omni_syntax_group_include_sql')
132ad3b366cSBram Moolenaar    let g:omni_syntax_group_include_sql = ''
133ad3b366cSBram Moolenaarendif
134ad3b366cSBram Moolenaarif !exists('g:omni_syntax_group_exclude_sql')
135ad3b366cSBram Moolenaar    let g:omni_syntax_group_exclude_sql = ''
136ad3b366cSBram Moolenaarendif
137e2f98b95SBram Moolenaarlet s:save_inc = g:omni_syntax_group_include_sql
138e2f98b95SBram Moolenaarlet s:save_exc = g:omni_syntax_group_exclude_sql
139e2f98b95SBram Moolenaar
140e2f98b95SBram Moolenaar" Used with the column list
141e2f98b95SBram Moolenaarlet s:save_prev_table       = ""
142e2f98b95SBram Moolenaar
143e2f98b95SBram Moolenaar" Default the option to verify table alias
144e2f98b95SBram Moolenaarif !exists('g:omni_sql_use_tbl_alias')
145e2f98b95SBram Moolenaar    let g:omni_sql_use_tbl_alias = 'a'
146e2f98b95SBram Moolenaarendif
147910f66f9SBram Moolenaar" Default syntax items to precache
148910f66f9SBram Moolenaarif !exists('g:omni_sql_precache_syntax_groups')
149910f66f9SBram Moolenaar    let g:omni_sql_precache_syntax_groups = [
150ad3b366cSBram Moolenaar                \ 'syntax\w*',
151ad3b366cSBram Moolenaar                \ 'sqlKeyword\w*',
152ad3b366cSBram Moolenaar                \ 'sqlFunction\w*',
153ad3b366cSBram Moolenaar                \ 'sqlOption\w*',
154ad3b366cSBram Moolenaar                \ 'sqlType\w*',
155ad3b366cSBram Moolenaar                \ 'sqlStatement\w*'
156910f66f9SBram Moolenaar                \ ]
157910f66f9SBram Moolenaarendif
158eb3593b3SBram Moolenaar" Set ignorecase to the ftplugin standard
159eb3593b3SBram Moolenaarif !exists('g:omni_sql_ignorecase')
160eb3593b3SBram Moolenaar    let g:omni_sql_ignorecase = &ignorecase
161eb3593b3SBram Moolenaarendif
162eb3593b3SBram Moolenaar" During table completion, should the table list also
163eb3593b3SBram Moolenaar" include the owner name
164eb3593b3SBram Moolenaarif !exists('g:omni_sql_include_owner')
165eb3593b3SBram Moolenaar    let g:omni_sql_include_owner = 0
166eb3593b3SBram Moolenaar    if exists('g:loaded_dbext')
167eb3593b3SBram Moolenaar        if g:loaded_dbext >= 300
168eb3593b3SBram Moolenaar            " New to dbext 3.00, by default the table lists include the owner
169eb3593b3SBram Moolenaar            " name of the table.  This is used when determining how much of
170eb3593b3SBram Moolenaar            " whatever has been typed should be replaced as part of the
171eb3593b3SBram Moolenaar            " code replacement.
172eb3593b3SBram Moolenaar            let g:omni_sql_include_owner = 1
173eb3593b3SBram Moolenaar        endif
174eb3593b3SBram Moolenaar    endif
175eb3593b3SBram Moolenaarendif
176db7207e6SBram Moolenaar" Default type of completion used when <C-X><C-O> is pressed
177db7207e6SBram Moolenaarif !exists('g:omni_sql_default_compl_type')
178db7207e6SBram Moolenaar    let g:omni_sql_default_compl_type = 'table'
179db7207e6SBram Moolenaarendif
180e2f98b95SBram Moolenaar
181e2f98b95SBram Moolenaar" This function is used for the 'omnifunc' option.
18234feacbcSBram Moolenaar" It is called twice by omni and it is responsible
18334feacbcSBram Moolenaar" for returning the completion list of items.
18434feacbcSBram Moolenaar" But it must also determine context of what to complete
18534feacbcSBram Moolenaar" and what to "replace" with the completion.
18634feacbcSBram Moolenaar" The a:base, is replaced directly with what the user
18734feacbcSBram Moolenaar" chooses from the choices.
18834feacbcSBram Moolenaar" The s:prepend provides context for the completion.
189e2f98b95SBram Moolenaarfunction! sqlcomplete#Complete(findstart, base)
190e2f98b95SBram Moolenaar
191e2f98b95SBram Moolenaar    " Default to table name completion
192e2f98b95SBram Moolenaar    let compl_type = 'table'
193e2f98b95SBram Moolenaar    " Allow maps to specify what type of object completion they want
194e2f98b95SBram Moolenaar    if exists('b:sql_compl_type')
195e2f98b95SBram Moolenaar        let compl_type = b:sql_compl_type
196e2f98b95SBram Moolenaar    endif
19734feacbcSBram Moolenaar    let begindot = 0
198e2f98b95SBram Moolenaar
199910f66f9SBram Moolenaar    " First pass through this function determines how much of the line should
200910f66f9SBram Moolenaar    " be replaced by whatever is chosen from the completion list
201e2f98b95SBram Moolenaar    if a:findstart
202e2f98b95SBram Moolenaar        " Locate the start of the item, including "."
203e2f98b95SBram Moolenaar        let line     = getline('.')
204e2f98b95SBram Moolenaar        let start    = col('.') - 1
205e2f98b95SBram Moolenaar        let lastword = -1
206f193fffdSBram Moolenaar        " Check if the first character is a ".", for column completion
207f193fffdSBram Moolenaar        if line[start - 1] == '.'
208f193fffdSBram Moolenaar            let begindot = 1
209f193fffdSBram Moolenaar        endif
210e2f98b95SBram Moolenaar        while start > 0
2115c73622aSBram Moolenaar            " Additional code was required to handle objects which
2125c73622aSBram Moolenaar            " can contain spaces like "my table name".
2135c73622aSBram Moolenaar            if line[start - 1] !~ '\(\w\|\.\)'
2145c73622aSBram Moolenaar                " If the previous character is not a period or word character
2155c73622aSBram Moolenaar                break
2165c73622aSBram Moolenaar            " elseif line[start - 1] =~ '\(\w\|\s\+\)'
2175c73622aSBram Moolenaar            "     let start -= 1
2185c73622aSBram Moolenaar            elseif line[start - 1] =~ '\w'
2195c73622aSBram Moolenaar                " If the previous character is word character continue back
220e2f98b95SBram Moolenaar                let start -= 1
221eb3593b3SBram Moolenaar            elseif line[start - 1] =~ '\.' &&
222eb3593b3SBram Moolenaar                        \ compl_type =~ 'column\|table\|view\|procedure'
2235c73622aSBram Moolenaar                " If the previous character is a period and we are completing
2245c73622aSBram Moolenaar                " an object which can be specified with a period like this:
2255c73622aSBram Moolenaar                "     table_name.column_name
2265c73622aSBram Moolenaar                "     owner_name.table_name
2275c73622aSBram Moolenaar
228eb3593b3SBram Moolenaar                " If lastword has already been set for column completion
229eb3593b3SBram Moolenaar                " break from the loop, since we do not also want to pickup
230eb3593b3SBram Moolenaar                " a table name if it was also supplied.
23134feacbcSBram Moolenaar                " Unless g:omni_sql_include_owner == 1, then we can
23234feacbcSBram Moolenaar                " include the ownername.
233f193fffdSBram Moolenaar                if lastword != -1 && compl_type == 'column'
23434feacbcSBram Moolenaar                            \ && g:omni_sql_include_owner == 0
235eb3593b3SBram Moolenaar                    break
236eb3593b3SBram Moolenaar                endif
237f193fffdSBram Moolenaar                " If column completion was specified stop at the "." if
238f193fffdSBram Moolenaar                " a . was specified, otherwise, replace all the way up
239f193fffdSBram Moolenaar                " to the owner name (if included).
240f193fffdSBram Moolenaar                if lastword == -1 && compl_type == 'column' && begindot == 1
241e2f98b95SBram Moolenaar                    let lastword = start
242e2f98b95SBram Moolenaar                endif
243eb3593b3SBram Moolenaar                " If omni_sql_include_owner = 0, do not include the table
244eb3593b3SBram Moolenaar                " name as part of the substitution, so break here
245eb3593b3SBram Moolenaar                if lastword == -1 &&
24634feacbcSBram Moolenaar                            \ compl_type =~ '\<\(table\|view\|procedure\|column\|column_csv\)\>' &&
247eb3593b3SBram Moolenaar                            \ g:omni_sql_include_owner == 0
248eb3593b3SBram Moolenaar                    let lastword = start
249eb3593b3SBram Moolenaar                    break
250eb3593b3SBram Moolenaar                endif
251e2f98b95SBram Moolenaar                let start -= 1
252e2f98b95SBram Moolenaar            else
253e2f98b95SBram Moolenaar                break
254e2f98b95SBram Moolenaar            endif
255e2f98b95SBram Moolenaar        endwhile
256e2f98b95SBram Moolenaar
257e2f98b95SBram Moolenaar        " Return the column of the last word, which is going to be changed.
258e2f98b95SBram Moolenaar        " Remember the text that comes before it in s:prepended.
259e2f98b95SBram Moolenaar        if lastword == -1
260e2f98b95SBram Moolenaar            let s:prepended = ''
261e2f98b95SBram Moolenaar            return start
262e2f98b95SBram Moolenaar        endif
263e2f98b95SBram Moolenaar        let s:prepended = strpart(line, start, lastword - start)
264e2f98b95SBram Moolenaar        return lastword
265e2f98b95SBram Moolenaar    endif
266e2f98b95SBram Moolenaar
267910f66f9SBram Moolenaar    " Second pass through this function will determine what data to put inside
268910f66f9SBram Moolenaar    " of the completion list
269910f66f9SBram Moolenaar    " s:prepended is set by the first pass
270e2f98b95SBram Moolenaar    let base = s:prepended . a:base
271e2f98b95SBram Moolenaar
272910f66f9SBram Moolenaar    " Default the completion list to an empty list
273e2f98b95SBram Moolenaar    let compl_list = []
274e2f98b95SBram Moolenaar
275e2f98b95SBram Moolenaar    " Default to table name completion
276db7207e6SBram Moolenaar    let compl_type = g:omni_sql_default_compl_type
277e2f98b95SBram Moolenaar    " Allow maps to specify what type of object completion they want
278e2f98b95SBram Moolenaar    if exists('b:sql_compl_type')
279e2f98b95SBram Moolenaar        let compl_type = b:sql_compl_type
280e2f98b95SBram Moolenaar        unlet b:sql_compl_type
281e2f98b95SBram Moolenaar    endif
282e2f98b95SBram Moolenaar
283e2f98b95SBram Moolenaar    if compl_type == 'tableReset'
284e2f98b95SBram Moolenaar        let compl_type = 'table'
285e2f98b95SBram Moolenaar        let base = ''
286e2f98b95SBram Moolenaar    endif
287e2f98b95SBram Moolenaar
288e2f98b95SBram Moolenaar    if compl_type == 'table' ||
289e2f98b95SBram Moolenaar                \ compl_type == 'procedure' ||
290e2f98b95SBram Moolenaar                \ compl_type == 'view'
291e2f98b95SBram Moolenaar
292e2f98b95SBram Moolenaar        " This type of completion relies upon the dbext.vim plugin
293e2f98b95SBram Moolenaar        if s:SQLCCheck4dbext() == -1
294e2f98b95SBram Moolenaar            return []
295e2f98b95SBram Moolenaar        endif
296e2f98b95SBram Moolenaar
29783e138c6SBram Moolenaar        " Allow the user to override the dbext plugin to specify whether
29883e138c6SBram Moolenaar        " the owner/creator should be included in the list
2998c8de839SBram Moolenaar        if g:loaded_dbext >= 300
3008c8de839SBram Moolenaar            let saveSetting = DB_listOption('dict_show_owner')
3018c8de839SBram Moolenaar            exec 'DBSetOption dict_show_owner='.(g:omni_sql_include_owner==1?'1':'0')
302e2f98b95SBram Moolenaar        endif
30383e138c6SBram Moolenaar
30483e138c6SBram Moolenaar        let compl_type_uc = substitute(compl_type, '\w\+', '\u&', '')
3055c73622aSBram Moolenaar        " Same call below, no need to do it twice
3065c73622aSBram Moolenaar        " if s:sql_file_{compl_type} == ""
3075c73622aSBram Moolenaar        "     let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type_uc)
3085c73622aSBram Moolenaar        " endif
30983e138c6SBram Moolenaar        let s:sql_file_{compl_type} = DB_getDictionaryName(compl_type_uc)
310e2f98b95SBram Moolenaar        if s:sql_file_{compl_type} != ""
311e2f98b95SBram Moolenaar            if filereadable(s:sql_file_{compl_type})
312e2f98b95SBram Moolenaar                let compl_list = readfile(s:sql_file_{compl_type})
313e2f98b95SBram Moolenaar            endif
314e2f98b95SBram Moolenaar        endif
31583e138c6SBram Moolenaar
3168c8de839SBram Moolenaar        if g:loaded_dbext > 300
3178c8de839SBram Moolenaar            exec 'DBSetOption dict_show_owner='.saveSetting
3188c8de839SBram Moolenaar        endif
31983e138c6SBram Moolenaar    elseif compl_type =~? 'column'
320e2f98b95SBram Moolenaar
321e2f98b95SBram Moolenaar        " This type of completion relies upon the dbext.vim plugin
322e2f98b95SBram Moolenaar        if s:SQLCCheck4dbext() == -1
323e2f98b95SBram Moolenaar            return []
324e2f98b95SBram Moolenaar        endif
325e2f98b95SBram Moolenaar
326e2f98b95SBram Moolenaar        if base == ""
327e2f98b95SBram Moolenaar            " The last time we displayed a column list we stored
328e2f98b95SBram Moolenaar            " the table name.  If the user selects a column list
329e2f98b95SBram Moolenaar            " without a table name of alias present, assume they want
330e2f98b95SBram Moolenaar            " the previous column list displayed.
331e2f98b95SBram Moolenaar            let base = s:save_prev_table
332e2f98b95SBram Moolenaar        endif
333e2f98b95SBram Moolenaar
33483e138c6SBram Moolenaar        let owner  = ''
33583e138c6SBram Moolenaar        let column = ''
33683e138c6SBram Moolenaar
33783e138c6SBram Moolenaar        if base =~ '\.'
33883e138c6SBram Moolenaar            " Check if the owner/creator has been specified
33983e138c6SBram Moolenaar            let owner  = matchstr( base, '^\zs.*\ze\..*\..*' )
34083e138c6SBram Moolenaar            let table  = matchstr( base, '^\(.*\.\)\?\zs.*\ze\..*' )
34183e138c6SBram Moolenaar            let column = matchstr( base, '.*\.\zs.*' )
34283e138c6SBram Moolenaar
34334feacbcSBram Moolenaar            if g:omni_sql_include_owner == 1 && owner == '' && table != '' && column != ''
34434feacbcSBram Moolenaar                let owner  = table
34534feacbcSBram Moolenaar                let table  = column
34634feacbcSBram Moolenaar                let column = ''
34734feacbcSBram Moolenaar            endif
34834feacbcSBram Moolenaar
34983e138c6SBram Moolenaar            " It is pretty well impossible to determine if the user
35083e138c6SBram Moolenaar            " has entered:
35183e138c6SBram Moolenaar            "    owner.table
35283e138c6SBram Moolenaar            "    table.column_prefix
35383e138c6SBram Moolenaar            " So there are a couple of things we can do to mitigate
35483e138c6SBram Moolenaar            " this issue.
35583e138c6SBram Moolenaar            "    1.  Check if the dbext plugin has the option turned
35683e138c6SBram Moolenaar            "        on to even allow owners
35783e138c6SBram Moolenaar            "    2.  Based on 1, if the user is showing a table list
35800a927d6SBram Moolenaar            "        and the DrillIntoTable (using <Right>) then
35983e138c6SBram Moolenaar            "        this will be owner.table.  In this case, we can
36083e138c6SBram Moolenaar            "        check to see the table.column exists in the
36183e138c6SBram Moolenaar            "        cached table list.  If it does, then we have
36283e138c6SBram Moolenaar            "        determined the user has actually chosen
36383e138c6SBram Moolenaar            "        owner.table, not table.column_prefix.
36483e138c6SBram Moolenaar            let found = -1
36583e138c6SBram Moolenaar            if g:omni_sql_include_owner == 1 && owner == ''
36683e138c6SBram Moolenaar                if filereadable(s:sql_file_table)
36783e138c6SBram Moolenaar                    let tbl_list = readfile(s:sql_file_table)
36883e138c6SBram Moolenaar                    let found    = index( tbl_list, ((table != '')?(table.'.'):'').column)
36983e138c6SBram Moolenaar                endif
37083e138c6SBram Moolenaar            endif
37183e138c6SBram Moolenaar            " If the table.column was found in the table list, we can safely assume
37283e138c6SBram Moolenaar            " the owner was not provided and shift the items appropriately.
37383e138c6SBram Moolenaar            " OR
37483e138c6SBram Moolenaar            " If the user has indicated not to use table owners at all and
37583e138c6SBram Moolenaar            " the base ends in a '.' we know they are not providing a column
37683e138c6SBram Moolenaar            " name, so we can shift the items appropriately.
377db7207e6SBram Moolenaar            " if found != -1 || (g:omni_sql_include_owner == 0 && base !~ '\.$')
378db7207e6SBram Moolenaar            "     let owner  = table
379db7207e6SBram Moolenaar            "     let table  = column
380db7207e6SBram Moolenaar            "     let column = ''
381db7207e6SBram Moolenaar            " endif
38283e138c6SBram Moolenaar        else
383db7207e6SBram Moolenaar            " If no "." was provided and the user asked for
384db7207e6SBram Moolenaar            " column level completion, first attempt the match
385db7207e6SBram Moolenaar            " on any previous column lists.  If the user asked
386db7207e6SBram Moolenaar            " for a list of columns comma separated, continue as usual.
387db7207e6SBram Moolenaar            if compl_type == 'column' && s:save_prev_table != ''
388db7207e6SBram Moolenaar                " The last time we displayed a column list we stored
389db7207e6SBram Moolenaar                " the table name.  If the user selects a column list
390db7207e6SBram Moolenaar                " without a table name of alias present, assume they want
391db7207e6SBram Moolenaar                " the previous column list displayed.
392db7207e6SBram Moolenaar                let table     = s:save_prev_table
393db7207e6SBram Moolenaar                let list_type = ''
394db7207e6SBram Moolenaar
395db7207e6SBram Moolenaar                let compl_list  = s:SQLCGetColumns(table, list_type)
396db7207e6SBram Moolenaar                if ! empty(compl_list)
397db7207e6SBram Moolenaar                    " If no column prefix has been provided and the table
398db7207e6SBram Moolenaar                    " name was provided, append it to each of the items
399db7207e6SBram Moolenaar                    " returned.
400db7207e6SBram Moolenaar                    let compl_list = filter(deepcopy(compl_list), 'v:val=~"^'.base.'"' )
401db7207e6SBram Moolenaar
402db7207e6SBram Moolenaar                    " If not empty, we have a match on columns
403db7207e6SBram Moolenaar                    " return the list
404db7207e6SBram Moolenaar                    if ! empty(compl_list)
405db7207e6SBram Moolenaar                        return compl_list
406db7207e6SBram Moolenaar                    endif
407db7207e6SBram Moolenaar                endif
408db7207e6SBram Moolenaar            endif
409db7207e6SBram Moolenaar            " Since no columns were found to match the base supplied
410db7207e6SBram Moolenaar            " assume the user is trying to complete the column list
411db7207e6SBram Moolenaar            " for a table (and or an alias to a table).
41283e138c6SBram Moolenaar            let table  = base
41383e138c6SBram Moolenaar        endif
41483e138c6SBram Moolenaar
41583e138c6SBram Moolenaar        " Get anything after the . and consider this the table name
41683e138c6SBram Moolenaar        " If an owner has been specified, then we must consider the
41783e138c6SBram Moolenaar        " base to be a partial column name
41883e138c6SBram Moolenaar        " let base  = matchstr( base, '^\(.*\.\)\?\zs.*' )
41983e138c6SBram Moolenaar
42083e138c6SBram Moolenaar        if table != ""
421e2f98b95SBram Moolenaar            let s:save_prev_table = base
42283e138c6SBram Moolenaar            let list_type         = ''
42383e138c6SBram Moolenaar
42483e138c6SBram Moolenaar            if compl_type == 'column_csv'
42583e138c6SBram Moolenaar                " Return one array element, with a comma separated
42683e138c6SBram Moolenaar                " list of values instead of multiple array entries
42783e138c6SBram Moolenaar                " for each column in the table.
42883e138c6SBram Moolenaar                let list_type     = 'csv'
42983e138c6SBram Moolenaar            endif
43083e138c6SBram Moolenaar
43134feacbcSBram Moolenaar            " If we are including the OWNER for the objects, then for
43234feacbcSBram Moolenaar            " table completion, if we have it, it should be included
43334feacbcSBram Moolenaar            " as there can be the same table names in a database yet
43434feacbcSBram Moolenaar            " with different owner names.
43534feacbcSBram Moolenaar            if g:omni_sql_include_owner == 1 && owner != '' && table != ''
43634feacbcSBram Moolenaar                let compl_list  = s:SQLCGetColumns(owner.'.'.table, list_type)
43734feacbcSBram Moolenaar            else
43883e138c6SBram Moolenaar                let compl_list  = s:SQLCGetColumns(table, list_type)
43934feacbcSBram Moolenaar            endif
44034feacbcSBram Moolenaar
44183e138c6SBram Moolenaar            if column != ''
44283e138c6SBram Moolenaar                " If no column prefix has been provided and the table
44383e138c6SBram Moolenaar                " name was provided, append it to each of the items
44483e138c6SBram Moolenaar                " returned.
445db7207e6SBram Moolenaar                let compl_list = map(compl_list, 'table.".".v:val')
44683e138c6SBram Moolenaar                if owner != ''
44783e138c6SBram Moolenaar                    " If an owner has been provided append it to each of the
44883e138c6SBram Moolenaar                    " items returned.
449db7207e6SBram Moolenaar                    let compl_list = map(compl_list, 'owner.".".v:val')
45083e138c6SBram Moolenaar                endif
45183e138c6SBram Moolenaar            else
452e2f98b95SBram Moolenaar                let base = ''
453e2f98b95SBram Moolenaar            endif
454e2f98b95SBram Moolenaar
45583e138c6SBram Moolenaar            if compl_type == 'column_csv'
456e2f98b95SBram Moolenaar                " Join the column array into 1 single element array
457e2f98b95SBram Moolenaar                " but make the columns column separated
458e2f98b95SBram Moolenaar                let compl_list        = [join(compl_list, ', ')]
45983e138c6SBram Moolenaar            endif
460e2f98b95SBram Moolenaar        endif
461e2f98b95SBram Moolenaar    elseif compl_type == 'resetCache'
462e2f98b95SBram Moolenaar        " Reset all cached items
463e2f98b95SBram Moolenaar        let s:tbl_name           = []
464e2f98b95SBram Moolenaar        let s:tbl_alias          = []
465e2f98b95SBram Moolenaar        let s:tbl_cols           = []
466e2f98b95SBram Moolenaar        let s:syn_list           = []
467e2f98b95SBram Moolenaar        let s:syn_value          = []
4685e9b2fa9SBram Moolenaar
4695e9b2fa9SBram Moolenaar        if s:sql_file_table != ""
4705e9b2fa9SBram Moolenaar            if g:loaded_dbext >= 2300
4715e9b2fa9SBram Moolenaar                call DB_DictionaryDelete("table")
4725e9b2fa9SBram Moolenaar            else
4735e9b2fa9SBram Moolenaar                DBCompleteTables!
4745e9b2fa9SBram Moolenaar            endif
4755e9b2fa9SBram Moolenaar        endif
4765e9b2fa9SBram Moolenaar        if s:sql_file_procedure != ""
4775e9b2fa9SBram Moolenaar            if g:loaded_dbext >= 2300
4785e9b2fa9SBram Moolenaar                call DB_DictionaryDelete("procedure")
4795e9b2fa9SBram Moolenaar            else
4805e9b2fa9SBram Moolenaar                DBCompleteProcedures!
4815e9b2fa9SBram Moolenaar            endif
4825e9b2fa9SBram Moolenaar        endif
4835e9b2fa9SBram Moolenaar        if s:sql_file_view != ""
4845e9b2fa9SBram Moolenaar            if g:loaded_dbext >= 2300
4855e9b2fa9SBram Moolenaar                call DB_DictionaryDelete("view")
4865e9b2fa9SBram Moolenaar            else
4875e9b2fa9SBram Moolenaar                DBCompleteViews!
4885e9b2fa9SBram Moolenaar            endif
4895e9b2fa9SBram Moolenaar        endif
4905e9b2fa9SBram Moolenaar
49134feacbcSBram Moolenaar        let s:sql_file_table     = ""
49234feacbcSBram Moolenaar        let s:sql_file_procedure = ""
49334feacbcSBram Moolenaar        let s:sql_file_view      = ""
494f193fffdSBram Moolenaar
495f193fffdSBram Moolenaar        let msg = "All SQL cached items have been removed."
496f193fffdSBram Moolenaar        call s:SQLCWarningMsg(msg)
497f193fffdSBram Moolenaar        " Leave time for the user to read the error message
498f193fffdSBram Moolenaar        :sleep 2
499e2f98b95SBram Moolenaar    else
500910f66f9SBram Moolenaar        let compl_list = s:SQLCGetSyntaxList(compl_type)
501e2f98b95SBram Moolenaar    endif
502e2f98b95SBram Moolenaar
503e2f98b95SBram Moolenaar    if base != ''
5045c73622aSBram Moolenaar        " Filter the list based on the first few characters the user entered.
5055c73622aSBram Moolenaar        " Check if the text matches at the beginning
506db7207e6SBram Moolenaar        "         \\(^.base.'\\)
5075c73622aSBram Moolenaar        " or
5085c73622aSBram Moolenaar        " Match to a owner.table or alias.column type match
509db7207e6SBram Moolenaar        "         ^\\(\\w\\+\\.\\)\\?'.base.'\\)
5105c73622aSBram Moolenaar        " or
5115c73622aSBram Moolenaar        " Handle names with spaces "my table name"
512db7207e6SBram Moolenaar        "         "\\(^'.base.'\\|^\\(\\w\\+\\.\\)\\?'.base.'\\)"'
513db7207e6SBram Moolenaar        "
5145c73622aSBram Moolenaar        let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|^\\(\\w\\+\\.\\)\\?'.base.'\\)"'
5155c73622aSBram Moolenaar        " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\)"'
5165c73622aSBram Moolenaar        " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|\\(\\.\\)\\?'.base.'\\)"'
5175c73622aSBram Moolenaar        " let expr = 'v:val '.(g:omni_sql_ignorecase==1?'=~?':'=~#').' "\\(^'.base.'\\|\\([^.]*\\)\\?'.base.'\\)"'
518eb3593b3SBram Moolenaar        let compl_list = filter(deepcopy(compl_list), expr)
51934feacbcSBram Moolenaar
52034feacbcSBram Moolenaar        if empty(compl_list) && compl_type == 'table' && base =~ '\.$'
52134feacbcSBram Moolenaar            " It is possible we could be looking for column name completion
52234feacbcSBram Moolenaar            " and the user simply hit C-X C-O to lets try it as well
52334feacbcSBram Moolenaar            " since we had no hits with the tables.
52434feacbcSBram Moolenaar            " If the base ends with a . it is hard to know if we are
52534feacbcSBram Moolenaar            " completing table names or column names.
52634feacbcSBram Moolenaar            let list_type = ''
52734feacbcSBram Moolenaar
52834feacbcSBram Moolenaar            let compl_list  = s:SQLCGetColumns(base, list_type)
52934feacbcSBram Moolenaar        endif
530e2f98b95SBram Moolenaar    endif
531e2f98b95SBram Moolenaar
532910f66f9SBram Moolenaar    if exists('b:sql_compl_savefunc') && b:sql_compl_savefunc != ""
533910f66f9SBram Moolenaar        let &omnifunc = b:sql_compl_savefunc
534910f66f9SBram Moolenaar    endif
535910f66f9SBram Moolenaar
53634feacbcSBram Moolenaar    if empty(compl_list)
53734feacbcSBram Moolenaar        call s:SQLCWarningMsg( 'Could not find type['.compl_type.'] using prepend[.'.s:prepended.'] base['.a:base.']' )
53834feacbcSBram Moolenaar    endif
53934feacbcSBram Moolenaar
540e2f98b95SBram Moolenaar    return compl_list
541e2f98b95SBram Moolenaarendfunc
542e2f98b95SBram Moolenaar
543910f66f9SBram Moolenaarfunction! sqlcomplete#PreCacheSyntax(...)
544910f66f9SBram Moolenaar    let syn_group_arr = []
545f9d5ca1dSBram Moolenaar    let syn_items     = []
546f9d5ca1dSBram Moolenaar
547910f66f9SBram Moolenaar    if a:0 > 0
548f9d5ca1dSBram Moolenaar        if type(a:1) != 3
549f9d5ca1dSBram Moolenaar            call s:SQLCWarningMsg("Parameter is not a list. Example:['syntaxGroup1', 'syntaxGroup2']")
550f9d5ca1dSBram Moolenaar            return ''
551f9d5ca1dSBram Moolenaar        endif
552910f66f9SBram Moolenaar        let syn_group_arr = a:1
553910f66f9SBram Moolenaar    else
554910f66f9SBram Moolenaar        let syn_group_arr = g:omni_sql_precache_syntax_groups
555910f66f9SBram Moolenaar    endif
55683e138c6SBram Moolenaar    " For each group specified in the list, precache all
557*6c391a74SBram Moolenaar    " the syntax items.
558910f66f9SBram Moolenaar    if !empty(syn_group_arr)
559910f66f9SBram Moolenaar        for group_name in syn_group_arr
560f9d5ca1dSBram Moolenaar            let syn_items = extend( syn_items, s:SQLCGetSyntaxList(group_name) )
561f9d5ca1dSBram Moolenaar        endfor
562f9d5ca1dSBram Moolenaar    endif
563f9d5ca1dSBram Moolenaar
564f9d5ca1dSBram Moolenaar    return syn_items
565f9d5ca1dSBram Moolenaarendfunction
566f9d5ca1dSBram Moolenaar
567f9d5ca1dSBram Moolenaarfunction! sqlcomplete#ResetCacheSyntax(...)
568f9d5ca1dSBram Moolenaar    let syn_group_arr = []
569f9d5ca1dSBram Moolenaar
570f9d5ca1dSBram Moolenaar    if a:0 > 0
571f9d5ca1dSBram Moolenaar        if type(a:1) != 3
572f9d5ca1dSBram Moolenaar            call s:SQLCWarningMsg("Parameter is not a list. Example:['syntaxGroup1', 'syntaxGroup2']")
573f9d5ca1dSBram Moolenaar            return ''
574f9d5ca1dSBram Moolenaar        endif
575f9d5ca1dSBram Moolenaar        let syn_group_arr = a:1
576f9d5ca1dSBram Moolenaar    else
577f9d5ca1dSBram Moolenaar        let syn_group_arr = g:omni_sql_precache_syntax_groups
578f9d5ca1dSBram Moolenaar    endif
579f9d5ca1dSBram Moolenaar    " For each group specified in the list, precache all
580*6c391a74SBram Moolenaar    " the syntax items.
581f9d5ca1dSBram Moolenaar    if !empty(syn_group_arr)
582f9d5ca1dSBram Moolenaar        for group_name in syn_group_arr
583f9d5ca1dSBram Moolenaar            let list_idx = index(s:syn_list, group_name, 0, &ignorecase)
584f9d5ca1dSBram Moolenaar            if list_idx > -1
585f9d5ca1dSBram Moolenaar                " Remove from list of groups
586f9d5ca1dSBram Moolenaar                call remove( s:syn_list, list_idx )
587f9d5ca1dSBram Moolenaar                " Remove from list of keywords
588f9d5ca1dSBram Moolenaar                call remove( s:syn_value, list_idx )
589f9d5ca1dSBram Moolenaar            endif
590910f66f9SBram Moolenaar        endfor
591910f66f9SBram Moolenaar    endif
592910f66f9SBram Moolenaarendfunction
593910f66f9SBram Moolenaar
594910f66f9SBram Moolenaarfunction! sqlcomplete#Map(type)
595910f66f9SBram Moolenaar    " Tell the SQL plugin what you want to complete
596910f66f9SBram Moolenaar    let b:sql_compl_type=a:type
597910f66f9SBram Moolenaar    " Record previous omnifunc, if the SQL completion
598910f66f9SBram Moolenaar    " is being used in conjunction with other filetype
599910f66f9SBram Moolenaar    " completion plugins
600910f66f9SBram Moolenaar    if &omnifunc != "" && &omnifunc != 'sqlcomplete#Complete'
601910f66f9SBram Moolenaar        " Record the previous omnifunc, the plugin
602910f66f9SBram Moolenaar        " will automatically set this back so that it
603910f66f9SBram Moolenaar        " does not interfere with other ftplugins settings
604910f66f9SBram Moolenaar        let b:sql_compl_savefunc=&omnifunc
605910f66f9SBram Moolenaar    endif
606910f66f9SBram Moolenaar    " Set the OMNI func for the SQL completion plugin
607910f66f9SBram Moolenaar    let &omnifunc='sqlcomplete#Complete'
608910f66f9SBram Moolenaarendfunction
609910f66f9SBram Moolenaar
610f193fffdSBram Moolenaarfunction! sqlcomplete#DrillIntoTable()
611f193fffdSBram Moolenaar    " If the omni popup window is visible
612f193fffdSBram Moolenaar    if pumvisible()
613f193fffdSBram Moolenaar        call sqlcomplete#Map('column')
614f193fffdSBram Moolenaar        " C-Y, makes the currently highlighted entry active
615f193fffdSBram Moolenaar        " and trigger the omni popup to be redisplayed
61600a927d6SBram Moolenaar        call feedkeys("\<C-Y>\<C-X>\<C-O>", 'n')
617f193fffdSBram Moolenaar    else
618f193fffdSBram Moolenaar	" If the popup is not visible, simple perform the normal
61900a927d6SBram Moolenaar	" key behaviour.
620*6c391a74SBram Moolenaar	" Must use exec since the key must be preceded by "\"
62100a927d6SBram Moolenaar	" or feedkeys will simply push each character of the string
62200a927d6SBram Moolenaar	" rather than the "key press".
62300a927d6SBram Moolenaar        exec 'call feedkeys("\'.g:ftplugin_sql_omni_key_right.'", "n")'
624f193fffdSBram Moolenaar    endif
625f193fffdSBram Moolenaar    return ""
626f193fffdSBram Moolenaarendfunction
627f193fffdSBram Moolenaar
628f193fffdSBram Moolenaarfunction! sqlcomplete#DrillOutOfColumns()
629f193fffdSBram Moolenaar    " If the omni popup window is visible
630f193fffdSBram Moolenaar    if pumvisible()
631f193fffdSBram Moolenaar        call sqlcomplete#Map('tableReset')
632f193fffdSBram Moolenaar        " Trigger the omni popup to be redisplayed
633f193fffdSBram Moolenaar        call feedkeys("\<C-X>\<C-O>")
634f193fffdSBram Moolenaar    else
635f193fffdSBram Moolenaar	" If the popup is not visible, simple perform the normal
63600a927d6SBram Moolenaar	" key behaviour.
637*6c391a74SBram Moolenaar	" Must use exec since the key must be preceded by "\"
63800a927d6SBram Moolenaar	" or feedkeys will simply push each character of the string
63900a927d6SBram Moolenaar	" rather than the "key press".
64000a927d6SBram Moolenaar        exec 'call feedkeys("\'.g:ftplugin_sql_omni_key_left.'", "n")'
641f193fffdSBram Moolenaar    endif
642f193fffdSBram Moolenaar    return ""
643f193fffdSBram Moolenaarendfunction
644f193fffdSBram Moolenaar
645f193fffdSBram Moolenaarfunction! s:SQLCWarningMsg(msg)
646f193fffdSBram Moolenaar    echohl WarningMsg
647f9d5ca1dSBram Moolenaar    echomsg 'SQLComplete:'.a:msg
648f193fffdSBram Moolenaar    echohl None
649f193fffdSBram Moolenaarendfunction
650f193fffdSBram Moolenaar
651f193fffdSBram Moolenaarfunction! s:SQLCErrorMsg(msg)
652f193fffdSBram Moolenaar    echohl ErrorMsg
653f9d5ca1dSBram Moolenaar    echomsg 'SQLComplete:'.a:msg
654f193fffdSBram Moolenaar    echohl None
655f193fffdSBram Moolenaarendfunction
656f193fffdSBram Moolenaar
657910f66f9SBram Moolenaarfunction! s:SQLCGetSyntaxList(syn_group)
658910f66f9SBram Moolenaar    let syn_group  = a:syn_group
659910f66f9SBram Moolenaar    let compl_list = []
660910f66f9SBram Moolenaar
661910f66f9SBram Moolenaar    " Check if we have already cached the syntax list
662910f66f9SBram Moolenaar    let list_idx = index(s:syn_list, syn_group, 0, &ignorecase)
663910f66f9SBram Moolenaar    if list_idx > -1
664910f66f9SBram Moolenaar        " Return previously cached value
665910f66f9SBram Moolenaar        let compl_list = s:syn_value[list_idx]
666910f66f9SBram Moolenaar    else
667ad3b366cSBram Moolenaar        let s:save_inc = g:omni_syntax_group_include_sql
668ad3b366cSBram Moolenaar        let s:save_exc = g:omni_syntax_group_exclude_sql
669ad3b366cSBram Moolenaar        let g:omni_syntax_group_include_sql = ''
670ad3b366cSBram Moolenaar        let g:omni_syntax_group_exclude_sql = ''
671ad3b366cSBram Moolenaar
672910f66f9SBram Moolenaar        " Request the syntax list items from the
673910f66f9SBram Moolenaar        " syntax completion plugin
674910f66f9SBram Moolenaar        if syn_group == 'syntax'
675910f66f9SBram Moolenaar            " Handle this special case.  This allows the user
676910f66f9SBram Moolenaar            " to indicate they want all the syntax items available,
677910f66f9SBram Moolenaar            " so do not specify a specific include list.
678ad3b366cSBram Moolenaar            let syn_value                       = syntaxcomplete#OmniSyntaxList()
679910f66f9SBram Moolenaar        else
680910f66f9SBram Moolenaar            " The user has specified a specific syntax group
681910f66f9SBram Moolenaar            let g:omni_syntax_group_include_sql = syn_group
682ad3b366cSBram Moolenaar            let syn_value                       = syntaxcomplete#OmniSyntaxList(syn_group)
683910f66f9SBram Moolenaar        endif
684910f66f9SBram Moolenaar        let g:omni_syntax_group_include_sql = s:save_inc
685910f66f9SBram Moolenaar        let g:omni_syntax_group_exclude_sql = s:save_exc
686910f66f9SBram Moolenaar        " Cache these values for later use
687910f66f9SBram Moolenaar        let s:syn_list  = add( s:syn_list,  syn_group )
688910f66f9SBram Moolenaar        let s:syn_value = add( s:syn_value, syn_value )
689910f66f9SBram Moolenaar        let compl_list  = syn_value
690910f66f9SBram Moolenaar    endif
691910f66f9SBram Moolenaar
692910f66f9SBram Moolenaar    return compl_list
693910f66f9SBram Moolenaarendfunction
694910f66f9SBram Moolenaar
695e2f98b95SBram Moolenaarfunction! s:SQLCCheck4dbext()
696e2f98b95SBram Moolenaar    if !exists('g:loaded_dbext')
697e2f98b95SBram Moolenaar        let msg = "The dbext plugin must be loaded for dynamic SQL completion"
698e2f98b95SBram Moolenaar        call s:SQLCErrorMsg(msg)
699e2f98b95SBram Moolenaar        " Leave time for the user to read the error message
700e2f98b95SBram Moolenaar        :sleep 2
701e2f98b95SBram Moolenaar        return -1
7028c8de839SBram Moolenaar    elseif g:loaded_dbext < 600
7038c8de839SBram Moolenaar        let msg = "The dbext plugin must be at least version 5.30 " .
704e2f98b95SBram Moolenaar                    \ " for dynamic SQL completion"
705e2f98b95SBram Moolenaar        call s:SQLCErrorMsg(msg)
706e2f98b95SBram Moolenaar        " Leave time for the user to read the error message
707e2f98b95SBram Moolenaar        :sleep 2
708e2f98b95SBram Moolenaar        return -1
709e2f98b95SBram Moolenaar    endif
710e2f98b95SBram Moolenaar    return 1
711e2f98b95SBram Moolenaarendfunction
712e2f98b95SBram Moolenaar
713e2f98b95SBram Moolenaarfunction! s:SQLCAddAlias(table_name, table_alias, cols)
714f193fffdSBram Moolenaar    " Strip off the owner if included
715f193fffdSBram Moolenaar    let table_name  = matchstr(a:table_name, '\%(.\{-}\.\)\?\zs\(.*\)' )
716e2f98b95SBram Moolenaar    let table_alias = a:table_alias
717e2f98b95SBram Moolenaar    let cols        = a:cols
718e2f98b95SBram Moolenaar
719e2f98b95SBram Moolenaar    if g:omni_sql_use_tbl_alias != 'n'
720e2f98b95SBram Moolenaar        if table_alias == ''
721e2f98b95SBram Moolenaar            if 'da' =~? g:omni_sql_use_tbl_alias
722e2f98b95SBram Moolenaar                if table_name =~ '_'
723e2f98b95SBram Moolenaar                    " Treat _ as separators since people often use these
724e2f98b95SBram Moolenaar                    " for word separators
725e2f98b95SBram Moolenaar                    let save_keyword = &iskeyword
726e2f98b95SBram Moolenaar                    setlocal iskeyword-=_
727e2f98b95SBram Moolenaar
728e2f98b95SBram Moolenaar                    " Get the first letter of each word
729e2f98b95SBram Moolenaar                    " [[:alpha:]] is used instead of \w
730e2f98b95SBram Moolenaar                    " to catch extended accented characters
731e2f98b95SBram Moolenaar                    "
732e2f98b95SBram Moolenaar                    let table_alias = substitute(
733e2f98b95SBram Moolenaar                                \ table_name,
734e2f98b95SBram Moolenaar                                \ '\<[[:alpha:]]\+\>_\?',
735e2f98b95SBram Moolenaar                                \ '\=strpart(submatch(0), 0, 1)',
736e2f98b95SBram Moolenaar                                \ 'g'
737e2f98b95SBram Moolenaar                                \ )
738e2f98b95SBram Moolenaar                    " Restore original value
739e2f98b95SBram Moolenaar                    let &iskeyword = save_keyword
740e2f98b95SBram Moolenaar                elseif table_name =~ '\u\U'
741f193fffdSBram Moolenaar                    let table_alias = substitute(
742e2f98b95SBram Moolenaar                                \ table_name, '\(\u\)\U*', '\1', 'g')
743e2f98b95SBram Moolenaar                else
744e2f98b95SBram Moolenaar                    let table_alias = strpart(table_name, 0, 1)
745e2f98b95SBram Moolenaar                endif
746e2f98b95SBram Moolenaar            endif
747e2f98b95SBram Moolenaar        endif
748e2f98b95SBram Moolenaar        if table_alias != ''
749e2f98b95SBram Moolenaar            " Following a word character, make sure there is a . and no spaces
750e2f98b95SBram Moolenaar            let table_alias = substitute(table_alias, '\w\zs\.\?\s*$', '.', '')
751e2f98b95SBram Moolenaar            if 'a' =~? g:omni_sql_use_tbl_alias && a:table_alias == ''
752e2f98b95SBram Moolenaar                let table_alias = inputdialog("Enter table alias:", table_alias)
753e2f98b95SBram Moolenaar            endif
754e2f98b95SBram Moolenaar        endif
755e2f98b95SBram Moolenaar        if table_alias != ''
756e2f98b95SBram Moolenaar            let cols = substitute(cols, '\<\w', table_alias.'&', 'g')
757e2f98b95SBram Moolenaar        endif
758e2f98b95SBram Moolenaar    endif
759e2f98b95SBram Moolenaar
760e2f98b95SBram Moolenaar    return cols
761e2f98b95SBram Moolenaarendfunction
762e2f98b95SBram Moolenaar
76383e138c6SBram Moolenaarfunction! s:SQLCGetObjectOwner(object)
76483e138c6SBram Moolenaar    " The owner regex matches a word at the start of the string which is
76583e138c6SBram Moolenaar    " followed by a dot, but doesn't include the dot in the result.
76683e138c6SBram Moolenaar    " ^           - from beginning of line
7678c8de839SBram Moolenaar    " \("\|\[\)\? - ignore any quotes
76883e138c6SBram Moolenaar    " \zs         - start the match now
7698c8de839SBram Moolenaar    " .\{-}       - get owner name
77083e138c6SBram Moolenaar    " \ze         - end the match
7718c8de839SBram Moolenaar    " \("\|\[\)\? - ignore any quotes
77283e138c6SBram Moolenaar    " \.          - must by followed by a .
7738c8de839SBram Moolenaar    " let owner = matchstr( a:object, '^\s*\zs.*\ze\.' )
7748c8de839SBram Moolenaar    let owner = matchstr( a:object, '^\("\|\[\)\?\zs\.\{-}\ze\("\|\]\)\?\.' )
77583e138c6SBram Moolenaar    return owner
77683e138c6SBram Moolenaarendfunction
77783e138c6SBram Moolenaar
778e2f98b95SBram Moolenaarfunction! s:SQLCGetColumns(table_name, list_type)
77934feacbcSBram Moolenaar    if a:table_name =~ '\.'
78034feacbcSBram Moolenaar        " Check if the owner/creator has been specified
78134feacbcSBram Moolenaar        let owner  = matchstr( a:table_name, '^\zs.*\ze\..*\..*' )
78234feacbcSBram Moolenaar        let table  = matchstr( a:table_name, '^\(.*\.\)\?\zs.*\ze\..*' )
78334feacbcSBram Moolenaar        let column = matchstr( a:table_name, '.*\.\zs.*' )
78434feacbcSBram Moolenaar
78534feacbcSBram Moolenaar        if g:omni_sql_include_owner == 1 && owner == '' && table != '' && column != ''
78634feacbcSBram Moolenaar            let owner  = table
78734feacbcSBram Moolenaar            let table  = column
78834feacbcSBram Moolenaar            let column = ''
78934feacbcSBram Moolenaar        endif
79034feacbcSBram Moolenaar    else
79134feacbcSBram Moolenaar        let owner  = ''
79234feacbcSBram Moolenaar        let table  = matchstr(a:table_name, '^["\[\]a-zA-Z0-9_ ]\+\ze\.\?')
79334feacbcSBram Moolenaar        let column = ''
79434feacbcSBram Moolenaar    endif
79534feacbcSBram Moolenaar
79683e138c6SBram Moolenaar    " Check if the table name was provided as part of the column name
79734feacbcSBram Moolenaar    " let table_name   = matchstr(a:table_name, '^["\[\]a-zA-Z0-9_ ]\+\ze\.\?')
79834feacbcSBram Moolenaar    let table_name   = table
799e2f98b95SBram Moolenaar    let table_cols   = []
800e2f98b95SBram Moolenaar    let table_alias  = ''
801e2f98b95SBram Moolenaar    let move_to_top  = 1
802e2f98b95SBram Moolenaar
8038c8de839SBram Moolenaar    let table_name   = substitute(table_name, '\s*\(.\{-}\)\s*$', '\1', 'g')
8048c8de839SBram Moolenaar
8058c8de839SBram Moolenaar    " If the table name was given as:
8068c8de839SBram Moolenaar    "     where c.
8078c8de839SBram Moolenaar    let table_name   = substitute(table_name, '^\c\(WHERE\|AND\|OR\)\s\+', '', '')
808eb3593b3SBram Moolenaar    if g:loaded_dbext >= 300
809e2f98b95SBram Moolenaar        let saveSettingAlias = DB_listOption('use_tbl_alias')
810e2f98b95SBram Moolenaar        exec 'DBSetOption use_tbl_alias=n'
811e2f98b95SBram Moolenaar    endif
812e2f98b95SBram Moolenaar
8138c8de839SBram Moolenaar    let table_name_stripped = substitute(table_name, '["\[\]]*', '', 'g')
8148c8de839SBram Moolenaar
815e2f98b95SBram Moolenaar    " Check if we have already cached the column list for this table
816e2f98b95SBram Moolenaar    " by its name
8178c8de839SBram Moolenaar    let list_idx = index(s:tbl_name, table_name_stripped, 0, &ignorecase)
818e2f98b95SBram Moolenaar    if list_idx > -1
8198c8de839SBram Moolenaar        let table_cols = split(s:tbl_cols[list_idx], '\n')
820e2f98b95SBram Moolenaar    else
821e2f98b95SBram Moolenaar        " Check if we have already cached the column list for this table
822e2f98b95SBram Moolenaar        " by its alias, assuming the table_name provided was actually
823e2f98b95SBram Moolenaar        " the alias for the table instead
824e2f98b95SBram Moolenaar        "     select *
825e2f98b95SBram Moolenaar        "       from area a
826e2f98b95SBram Moolenaar        "      where a.
8278c8de839SBram Moolenaar        let list_idx = index(s:tbl_alias, table_name_stripped, 0, &ignorecase)
828e2f98b95SBram Moolenaar        if list_idx > -1
8298c8de839SBram Moolenaar            let table_alias = table_name_stripped
830e2f98b95SBram Moolenaar            let table_name  = s:tbl_name[list_idx]
8318c8de839SBram Moolenaar            let table_cols  = split(s:tbl_cols[list_idx], '\n')
832e2f98b95SBram Moolenaar        endif
833e2f98b95SBram Moolenaar    endif
834e2f98b95SBram Moolenaar
835e2f98b95SBram Moolenaar    " If we have not found a cached copy of the table
836e2f98b95SBram Moolenaar    " And the table ends in a "." or we are looking for a column list
837e2f98b95SBram Moolenaar    " if list_idx == -1 && (a:table_name =~ '\.' || b:sql_compl_type =~ 'column')
838e2f98b95SBram Moolenaar    " if list_idx == -1 && (a:table_name =~ '\.' || a:list_type =~ 'csv')
839e2f98b95SBram Moolenaar    if list_idx == -1
840e2f98b95SBram Moolenaar         let saveY      = @y
841e2f98b95SBram Moolenaar         let saveSearch = @/
842e2f98b95SBram Moolenaar         let saveWScan  = &wrapscan
843e2f98b95SBram Moolenaar         let curline    = line(".")
844e2f98b95SBram Moolenaar         let curcol     = col(".")
845e2f98b95SBram Moolenaar
846*6c391a74SBram Moolenaar         " Do not let searches wrap
847e2f98b95SBram Moolenaar         setlocal nowrapscan
848e2f98b95SBram Moolenaar         " If . was entered, look at the word just before the .
849e2f98b95SBram Moolenaar         " We are looking for something like this:
850e2f98b95SBram Moolenaar         "    select *
851e2f98b95SBram Moolenaar         "      from customer c
852e2f98b95SBram Moolenaar         "     where c.
853e2f98b95SBram Moolenaar         " So when . is pressed, we need to find 'c'
854e2f98b95SBram Moolenaar         "
855e2f98b95SBram Moolenaar
856e2f98b95SBram Moolenaar         " Search backwards to the beginning of the statement
857e2f98b95SBram Moolenaar         " and do NOT wrap
858e2f98b95SBram Moolenaar         " exec 'silent! normal! v?\<\(select\|update\|delete\|;\)\>'."\n".'"yy'
85900a927d6SBram Moolenaar         exec 'silent! normal! ?\<\c\(select\|update\|delete\|;\)\>'."\n"
860e2f98b95SBram Moolenaar
861e2f98b95SBram Moolenaar         " Start characterwise visual mode
862e2f98b95SBram Moolenaar         " Advance right one character
86301164a65SBram Moolenaar         " Search forward until one of the following:
864e2f98b95SBram Moolenaar         "     1.  Another select/update/delete statement
865e2f98b95SBram Moolenaar         "     2.  A ; at the end of a line (the delimiter)
866e2f98b95SBram Moolenaar         "     3.  The end of the file (in case no delimiter)
867e2f98b95SBram Moolenaar         " Yank the visually selected text into the "y register.
86800a927d6SBram Moolenaar         exec 'silent! normal! vl/\c\(\<select\>\|\<update\>\|\<delete\>\|;\s*$\|\%$\)'."\n".'"yy'
869e2f98b95SBram Moolenaar
870e2f98b95SBram Moolenaar         let query = @y
871e2f98b95SBram Moolenaar         let query = substitute(query, "\n", ' ', 'g')
872e2f98b95SBram Moolenaar         let found = 0
873e2f98b95SBram Moolenaar
87400a927d6SBram Moolenaar         " if query =~? '^\c\(select\)'
87500a927d6SBram Moolenaar         if query =~? '^\(select\|update\|delete\)'
876e2f98b95SBram Moolenaar             let found = 1
877e2f98b95SBram Moolenaar             "  \(\(\<\w\+\>\)\.\)\?   -
87800a927d6SBram Moolenaar             " '\c\(from\|join\|,\).\{-}'  - Starting at the from clause (case insensitive)
879e2f98b95SBram Moolenaar             " '\zs\(\(\<\w\+\>\)\.\)\?' - Get the owner name (optional)
880e2f98b95SBram Moolenaar             " '\<\w\+\>\ze' - Get the table name
881e2f98b95SBram Moolenaar             " '\s\+\<'.table_name.'\>' - Followed by the alias
882e2f98b95SBram Moolenaar             " '\s*\.\@!.*'  - Cannot be followed by a .
883e2f98b95SBram Moolenaar             " '\(\<where\>\|$\)' - Must be followed by a WHERE clause
884e2f98b95SBram Moolenaar             " '.*'  - Exclude the rest of the line in the match
88500a927d6SBram Moolenaar             " let table_name_new = matchstr(@y,
88600a927d6SBram Moolenaar             "             \ '\c\(from\|join\|,\).\{-}'.
88700a927d6SBram Moolenaar             "             \ '\zs\(\("\|\[\)\?.\{-}\("\|\]\)\.\)\?'.
88800a927d6SBram Moolenaar             "             \ '\("\|\[\)\?.\{-}\("\|\]\)\?\ze'.
88900a927d6SBram Moolenaar             "             \ '\s\+\%(as\s\+\)\?\<'.
89000a927d6SBram Moolenaar             "             \ matchstr(table_name, '.\{-}\ze\.\?$').
89100a927d6SBram Moolenaar             "             \ '\>'.
89200a927d6SBram Moolenaar             "             \ '\s*\.\@!.*'.
89300a927d6SBram Moolenaar             "             \ '\(\<where\>\|$\)'.
89400a927d6SBram Moolenaar             "             \ '.*'
89500a927d6SBram Moolenaar             "             \ )
896db7207e6SBram Moolenaar             "
897db7207e6SBram Moolenaar             "
898db7207e6SBram Moolenaar             " ''\c\(\<from\>\|\<join\>\|,\)\s*'  - Starting at the from clause (case insensitive)
899db7207e6SBram Moolenaar             " '\zs\(\("\|\[\)\?\w\+\("\|\]\)\?\.\)\?' - Get the owner name (optional)
900db7207e6SBram Moolenaar             " '\("\|\[\)\?\w\+\("\|\]\)\?\ze' - Get the table name
901db7207e6SBram Moolenaar             " '\s\+\%(as\s\+\)\?\<'.matchstr(table_name, '.\{-}\ze\.\?$').'\>' - Followed by the alias
902db7207e6SBram Moolenaar             " '\s*\.\@!.*'  - Cannot be followed by a .
903db7207e6SBram Moolenaar             " '\(\<where\>\|$\)' - Must be followed by a WHERE clause
904db7207e6SBram Moolenaar             " '.*'  - Exclude the rest of the line in the match
905e2f98b95SBram Moolenaar             let table_name_new = matchstr(@y,
90600a927d6SBram Moolenaar                         \ '\c\(\<from\>\|\<join\>\|,\)\s*'.
90700a927d6SBram Moolenaar                         \ '\zs\(\("\|\[\)\?\w\+\("\|\]\)\?\.\)\?'.
90800a927d6SBram Moolenaar                         \ '\("\|\[\)\?\w\+\("\|\]\)\?\ze'.
909f193fffdSBram Moolenaar                         \ '\s\+\%(as\s\+\)\?\<'.
910f193fffdSBram Moolenaar                         \ matchstr(table_name, '.\{-}\ze\.\?$').
911f193fffdSBram Moolenaar                         \ '\>'.
912e2f98b95SBram Moolenaar                         \ '\s*\.\@!.*'.
913e2f98b95SBram Moolenaar                         \ '\(\<where\>\|$\)'.
914e2f98b95SBram Moolenaar                         \ '.*'
915e2f98b95SBram Moolenaar                         \ )
9168c8de839SBram Moolenaar
917e2f98b95SBram Moolenaar             if table_name_new != ''
918e2f98b95SBram Moolenaar                 let table_alias = table_name
91934feacbcSBram Moolenaar                 if g:omni_sql_include_owner == 1
92034feacbcSBram Moolenaar                    let table_name  = matchstr( table_name_new, '^\zs\(.\{-}\.\)\?\(.\{-}\.\)\?.*\ze' )
92134feacbcSBram Moolenaar                 else
92234feacbcSBram Moolenaar                     " let table_name  = matchstr( table_name_new, '^\(.*\.\)\?\zs.*\ze' )
92334feacbcSBram Moolenaar                    let table_name  = matchstr( table_name_new, '^\(.\{-}\.\)\?\zs\(.\{-}\.\)\?.*\ze' )
92434feacbcSBram Moolenaar                 endif
925e2f98b95SBram Moolenaar
926e2f98b95SBram Moolenaar                 let list_idx = index(s:tbl_name, table_name, 0, &ignorecase)
927e2f98b95SBram Moolenaar                 if list_idx > -1
928e2f98b95SBram Moolenaar                     let table_cols  = split(s:tbl_cols[list_idx])
929e2f98b95SBram Moolenaar                     let s:tbl_name[list_idx]  = table_name
930e2f98b95SBram Moolenaar                     let s:tbl_alias[list_idx] = table_alias
931e2f98b95SBram Moolenaar                 else
932e2f98b95SBram Moolenaar                     let list_idx = index(s:tbl_alias, table_name, 0, &ignorecase)
933e2f98b95SBram Moolenaar                     if list_idx > -1
934e2f98b95SBram Moolenaar                         let table_cols = split(s:tbl_cols[list_idx])
935e2f98b95SBram Moolenaar                         let s:tbl_name[list_idx]  = table_name
936e2f98b95SBram Moolenaar                         let s:tbl_alias[list_idx] = table_alias
937e2f98b95SBram Moolenaar                     endif
938e2f98b95SBram Moolenaar                 endif
939e2f98b95SBram Moolenaar
940e2f98b95SBram Moolenaar             endif
941e2f98b95SBram Moolenaar         else
942e2f98b95SBram Moolenaar             " Simply assume it is a table name provided with a . on the end
943e2f98b95SBram Moolenaar             let found = 1
944e2f98b95SBram Moolenaar         endif
945e2f98b95SBram Moolenaar
946e2f98b95SBram Moolenaar         let @y        = saveY
947e2f98b95SBram Moolenaar         let @/        = saveSearch
948e2f98b95SBram Moolenaar         let &wrapscan = saveWScan
949e2f98b95SBram Moolenaar
950e2f98b95SBram Moolenaar         " Return to previous location
951e2f98b95SBram Moolenaar         call cursor(curline, curcol)
952e2f98b95SBram Moolenaar
953e2f98b95SBram Moolenaar         if found == 0
954eb3593b3SBram Moolenaar             if g:loaded_dbext > 300
955e2f98b95SBram Moolenaar                 exec 'DBSetOption use_tbl_alias='.saveSettingAlias
956e2f98b95SBram Moolenaar             endif
957e2f98b95SBram Moolenaar
958e2f98b95SBram Moolenaar             " Not a SQL statement, do not display a list
959e2f98b95SBram Moolenaar             return []
960e2f98b95SBram Moolenaar         endif
961e2f98b95SBram Moolenaar    endif
962e2f98b95SBram Moolenaar
963e2f98b95SBram Moolenaar    if empty(table_cols)
964e2f98b95SBram Moolenaar        " Specify silent mode, no messages to the user (tbl, 1)
965e2f98b95SBram Moolenaar        " Specify do not comma separate (tbl, 1, 1)
96634feacbcSBram Moolenaar        " let table_cols_str = DB_getListColumn(table_name, 1, 1)
96734feacbcSBram Moolenaar        let table_cols_str = DB_getListColumn((owner!=''?owner.'.':'').table_name, 1, 1)
968e2f98b95SBram Moolenaar
969e2f98b95SBram Moolenaar        if table_cols_str != ""
970e2f98b95SBram Moolenaar            let s:tbl_name  = add( s:tbl_name,  table_name )
971e2f98b95SBram Moolenaar            let s:tbl_alias = add( s:tbl_alias, table_alias )
972e2f98b95SBram Moolenaar            let s:tbl_cols  = add( s:tbl_cols,  table_cols_str )
9738c8de839SBram Moolenaar            let table_cols  = split(table_cols_str, '\n')
974e2f98b95SBram Moolenaar        endif
975e2f98b95SBram Moolenaar
976e2f98b95SBram Moolenaar    endif
977e2f98b95SBram Moolenaar
978eb3593b3SBram Moolenaar    if g:loaded_dbext > 300
979e2f98b95SBram Moolenaar        exec 'DBSetOption use_tbl_alias='.saveSettingAlias
980e2f98b95SBram Moolenaar    endif
981e2f98b95SBram Moolenaar
982f193fffdSBram Moolenaar    " If the user has asked for a comma separate list of column
983f193fffdSBram Moolenaar    " values, ask the user if they want to prepend each column
984f193fffdSBram Moolenaar    " with a tablename alias.
985e2f98b95SBram Moolenaar    if a:list_type == 'csv' && !empty(table_cols)
986e2f98b95SBram Moolenaar        let cols       = join(table_cols, ', ')
987e2f98b95SBram Moolenaar        let cols       = s:SQLCAddAlias(table_name, table_alias, cols)
988e2f98b95SBram Moolenaar        let table_cols = [cols]
989e2f98b95SBram Moolenaar    endif
990e2f98b95SBram Moolenaar
991e2f98b95SBram Moolenaar    return table_cols
992e2f98b95SBram Moolenaarendfunction
99334feacbcSBram Moolenaar"  Restore:
99434feacbcSBram Moolenaarlet &cpo= s:keepcpo
99534feacbcSBram Moolenaarunlet s:keepcpo
99634feacbcSBram Moolenaar" vim: ts=4 fdm=marker
997