sqlcomplete.vim 38 KB

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