PageRenderTime 49ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 1ms

/autoload/vim_addon_sql.vim

https://github.com/MarcWeber/vim-addon-sql
Vim Script | 716 lines | 523 code | 90 blank | 103 comment | 104 complexity | 2c46b17e2c70dd4522aea1fed553ec70 MD5 | raw file
  1. " first implementation done on 2008 Sep 14 08:31:10 PM
  2. " After including this code in TOVL (which is deprecated) this code has a new
  3. " (final)
  4. " home in vim-addon-sql now.
  5. "
  6. " alternative plugins: SQLComplete and dbext (However the completion system
  7. " provided by this code is more accurate cause it only shows fields of all
  8. " talbes found in a query. What is a query? See ThisSQLCommand
  9. "
  10. " supported databases:
  11. " - MySQL
  12. " - postgresql
  13. " - sqlite (work in progress
  14. "
  15. " There are two windows:
  16. " a) the result window
  17. " b) the error window
  18. "
  19. " Mayb this code could be cleaned up ?
  20. " vam#DefineAndBind('s:config','g:vim_addon_sql','{}')
  21. if !exists('g:vim_addon_sql') | let g:vim_addon_sql = {} | endif | let s:c = g:vim_addon_sql
  22. let s:c['isql'] = get(s:c,'isql','isql')
  23. let s:c['mysql'] = get(s:c,'mysql','mysql')
  24. let s:c['psql'] = get(s:c,'psql','psql')
  25. let s:c['sqlite3'] = get(s:c,'sqlite3','sqlite3')
  26. let s:thisDir = expand('<sfile>:h')
  27. let s:mysqlFunctionsDump = s:thisDir.'/mysql-functions.dump'
  28. " retuns the text
  29. " a command is separated from other commands either
  30. " by empty lines or by ; (at the end of line)
  31. function! vim_addon_sql#ThisSQLCommand()
  32. if exists('b:thisSQLCommand')
  33. return b:thisSQLCommand()
  34. endif
  35. let nr = line('.')
  36. let up = nr -1
  37. let down = nr
  38. while up > 0 && getline(up) !~ ';$\|^\s*$'
  39. let up = up - 1
  40. endwhile
  41. " stop if ; is found
  42. " stop on empty line unless its current line which may be empty when
  43. " completing
  44. while getline(down) !~ ';$'
  45. \ && down <= line('$') &&
  46. \ ( down == line('.') || getline(down) !~ '^\s*$')
  47. let down = down + 1
  48. endwhile
  49. return getline(up+1,down)
  50. endfunction
  51. fun! vim_addon_sql#RunAndShow(sql)
  52. let result = b:db_conn.query(a:sql)
  53. TScratch 'scratch': '__SQL_RESULT__'
  54. call append(line('$'), ['',''])
  55. call append(line('$'), a:sql)
  56. call append(line('$'), split(result,"\n"))
  57. normal G
  58. wincmd w
  59. endf
  60. function! vim_addon_sql#UI()
  61. nnoremap <buffer> <F2> :call vim_addon_sql#RunAndShow(join(vim_addon_sql#ThisSQLCommand(),"\n"))<cr>
  62. vnoremap <buffer> <F2> y:echo b:db_conn.query(@")<cr>
  63. endfunction
  64. let s:error_buf_name = '__SQL_ERROR__'
  65. fun! s:ShowError(err)
  66. exec "TScratch 'scratch':'".s:error_buf_name."'"
  67. normal ggdG
  68. call append(line('$'), split(a:err,"\n"))
  69. endf
  70. fun! s:ClearError(result)
  71. let r = bufnr('__SQL_ERROR__')
  72. if r > 0
  73. exec r.'bw'
  74. endif
  75. return a:result
  76. endf
  77. " duplicate code, also found in TOVL {{{1
  78. fun! vim_addon_sql#SplitCurrentLineAtCursor()
  79. let pos = col('.') -1
  80. let line = getline('.')
  81. return [strpart(line,0,pos), strpart(line, pos, len(line)-pos)]
  82. endfunction
  83. "|func returns a list of all matches of the regex
  84. function! vim_addon_sql#MatchAll(str, regex)
  85. let matches = []
  86. let s = a:str
  87. while 1
  88. let pos = match(s,a:regex)
  89. if pos == -1
  90. return matches
  91. else
  92. let match = matchstr(s, a:regex)
  93. call add(matches, match)
  94. let s = strpart(s,strlen(match)+pos)
  95. endif
  96. endwhile
  97. endfunction
  98. fun! vim_addon_sql#Intersection(a,b)
  99. let result = []
  100. for i in a:a
  101. if index(a:b, i) != -1
  102. call add(result, i)
  103. endif
  104. endfor
  105. return result
  106. endf
  107. " combination of map and filter
  108. function! vim_addon_sql#MapIf(list, pred, expr)
  109. let result = []
  110. for Val in a:list
  111. exec 'let p = ('.a:pred.')'
  112. exec 'if p | call add(result, '.a:expr.')|endif'
  113. endfor
  114. return result
  115. endfunction
  116. fun! vim_addon_sql#EscapeShArg(arg)
  117. " zsh requires []
  118. return escape(a:arg, ";()*<>| '\"\\`[]&")
  119. endf
  120. " usage: vim_addon_sql#System( ['echo', 'foo'], {'stdin-text' : 'will be ignored by echo', status : 0 })
  121. fun! vim_addon_sql#System(items, ... )
  122. let opts = a:0 > 0 ? a:1 : {}
  123. let cmd = ''
  124. for a in a:items
  125. let cmd .= ' '.vim_addon_sql#EscapeShArg(a)
  126. endfor
  127. if has_key(opts, 'stdin-text')
  128. let f = tempname()
  129. " don't know why writefile(["line 1\nline 2"], f, 'b') has a different
  130. " result?
  131. call writefile(split(opts['stdin-text'],"\n"), f, 'b')
  132. let cmd = cmd. ' < '.f
  133. " call s:Log(1, 'executing system command: '.cmd.' first 2000 chars of stdin are :'.opts['stdin-text'][:2000])
  134. else
  135. " call s:Log(1, 'executing system command: '.cmd)
  136. endif
  137. let result = system(cmd .' 2>&1')
  138. "if exists('f') | call delete(f) | endif
  139. let g:systemResult = result
  140. let s = get(opts,'status',0)
  141. if v:shell_error != s && ( type(s) != type('') || s != '*' )
  142. let g:systemResult = result
  143. throw "command ".cmd."failed with exit code ".v:shell_error
  144. \ . " but ".s." expected. Have a look at the program output with :echo g:systemResult".repeat(' ',400)
  145. \ . " the first 500 chars of the result are \n".strpart(result,0,500)
  146. endif
  147. return result
  148. endfun
  149. " =========== completion =============================================
  150. fun! s:Match(s)
  151. return a:s =~ s:base || s:additional_regex != "" && a:s =~ s:additional_regex
  152. endf
  153. " what one of [ identifier, module ]
  154. function! vim_addon_sql#Complete(findstart, base)
  155. "findstart = 1 when we need to get the text length
  156. if a:findstart == 1
  157. let [bc,ac] = vim_addon_sql#SplitCurrentLineAtCursor()
  158. return len(bc)-len(matchstr(bc,'\%(\a\|\.\|_\|\d\)*$'))
  159. "findstart = 0 when we need to return the list of completions
  160. else
  161. if !exists('b:db_conn')
  162. echoe "b:db_conn not set, call vim_addon_sql#Connect(dbType,settings) to setup the connection"
  163. return []
  164. endif
  165. let text = vim_addon_sql#ThisSQLCommand()
  166. let words = split(join(text,"\n"),"[\n\r \t'\"()\\[\\],;]")
  167. let tables = b:db_conn.tables()
  168. let l = matchlist(a:base,'\([^.]*\)\.\([^.]*\)')
  169. if len(l) > 2
  170. let alias = l[1]
  171. let aliasP = alias.'.'
  172. let base = l[2]
  173. else
  174. let alias = ''
  175. let aliasP = ''
  176. let base = a:base
  177. endif
  178. let s:base = a:base[len(aliasP):]
  179. let s:additional_regex = ""
  180. let patterns = vim_addon_completion#AdditionalCompletionMatchPatterns(s:base
  181. \ , "vim_dev_plugin_completion_func", {'match_beginning_of_string': 0})
  182. let s:additional_regex = get(patterns, 'vim_regex', "")
  183. if get(b:db_conn, 'fields_ignore_case', 0)
  184. let s:base = '\c'.s:base
  185. let s:additional_regex = '\c'.s:additional_regex
  186. endif
  187. let tr = b:db_conn['regex']['table']
  188. let pat = '\zs\('.tr.'\)\s\+\cas\C\s\+\('.tr.'\)\ze'
  189. let pat2 = b:db_conn['regex']['table_from_match']
  190. let aliases = {}
  191. for aliasstr in vim_addon_sql#MatchAll(join(text,"\n"),pat)
  192. let l = matchlist(aliasstr, pat)
  193. if len(l) > 2
  194. let aliases[matchstr(l[2],pat2)] = matchstr(l[1], pat2)
  195. endif
  196. endfor
  197. let [bc,ac] = vim_addon_sql#SplitCurrentLineAtCursor()
  198. " before AS or after SELECT ... FROM, INSERT INTO .. CREATE / DROP / ALTER TABLE only table names will be shown
  199. let tablesOnly = (bc =~ '\c\%(FROM[^(]*\s\+\|JOIN\s\+\|INTO\s\+\|TABLE\s\+\)\C$' && bc !~ '\cWHERE' ) || ac =~ '^\s*as\>'
  200. if ! tablesOnly
  201. " field completion
  202. let table = get(aliases, alias,'')
  203. if alias != '' && table == ''
  204. let noAliasMatchWarning = ' ! alias not defined or table not found'
  205. else
  206. let noAliasMatchWarning = ''
  207. endif
  208. if table == ''
  209. let usedTables = vim_addon_sql#Intersection(tables, words)
  210. else
  211. let usedTables = [table]
  212. endif
  213. let g:usedTables = usedTables
  214. let fields = []
  215. for table in usedTables
  216. for f in b:db_conn['fields'](table)
  217. if s:Match(f)
  218. call complete_add({'word' : aliasP.f, 'abbr' : f, 'menu' : 'field of '.table.noAliasMatchWarning, 'dup': 1})
  219. endif
  220. endfor
  221. call complete_check()
  222. endfor
  223. endif
  224. if alias == '' && !(bc !~ '\cFROM' && ac =~ '\cFROM')
  225. for t in tables
  226. if s:Match(t)
  227. call complete_add({'word' : t, 'menu' : 'a table'})
  228. endif
  229. endfor
  230. endif
  231. if alias == '' && exists('b:db_conn.extraCompletions')
  232. call b:db_conn.extraCompletions()
  233. endif
  234. return []
  235. endif
  236. endfunction
  237. " =========== selecting dbs ==========================================
  238. " of course it's not a real "connection". It's an object which knows how to
  239. " run the cmd line tools
  240. function! vim_addon_sql#Connect(dbType,settings)
  241. let types = {
  242. \ 'mysql' : function('vim_addon_sql#MysqlConn'),
  243. \ 'pg' : function('vim_addon_sql#PostgresConn'),
  244. \ 'sqlite' : function('vim_addon_sql#SqliteConn'),
  245. \ 'firebird' : function('vim_addon_sql#FirebirdConn')
  246. \ }
  247. let b:db_conn = types[a:dbType](a:settings)
  248. endfunction
  249. " the following functions (only MySQL, Postgresql, SQLite implemented yet) all return
  250. " an "object" having the function
  251. " query(sql) runs any query and returns the result from stderr and stdout
  252. " tables() list of tables
  253. " fields(table) list of fields of the given table
  254. " invalidateSchema() removes cached schema data
  255. " schema returns the schema
  256. " regex.table : regex matching a table identifier
  257. " MySQL implementation {{{1
  258. " conn = attribute set
  259. " user :
  260. " password :
  261. " database : (optional)
  262. " optional:
  263. " host :
  264. " port :
  265. " or
  266. " cmd : (this way you can even use ssh mysql ...)
  267. function! vim_addon_sql#MysqlConn(conn)
  268. let conn = a:conn
  269. let conn['regex'] = {
  270. \ 'table' :'\%(`[^`]\+`\|[^ \t\r\n,;`]\+\)'
  271. \ , 'table_from_match' :'^`\?\zs[^`\n\r]*\ze`\?$'
  272. \ }
  273. if ! has_key(conn,'cmd')
  274. let cmd=[s:c.mysql]
  275. if (has_key(conn, 'socket'))
  276. call add(cmd, '-S'.conn['socket'])
  277. endif
  278. if has_key(conn, 'host')
  279. call add(cmd,'-h') | call add(cmd,conn['host'])
  280. endif
  281. if has_key(conn, 'port')
  282. call add(cmd,'-P') | call add(cmd,conn['port'])
  283. endif
  284. if has_key(conn, 'user')
  285. call add(cmd,'-u') | call add(cmd,conn['user'])
  286. endif
  287. if has_key(conn, 'password')
  288. call add(cmd,'--password='.conn['password'])
  289. endif
  290. if has_key(conn, 'extra_args')
  291. call add(cmd,conn['extra_args'])
  292. endif
  293. call add(cmd, '--default-character-set=utf8')
  294. let conn['cmd'] = cmd
  295. endif
  296. fun! conn.extraCompletions()
  297. " TODO add Postgresql function names
  298. if !exists('self["functions"]')
  299. let self['functions'] = eval(readfile(s:mysqlFunctionsDump)[0])
  300. endif
  301. for [d,v] in items(self['functions'])
  302. if s:Match(d)
  303. let args = d.'('. v.args .')'
  304. call complete_add({'word': d.(v.args == '' ? '' : '(')
  305. \ ,'menu': args
  306. \ ,'info': args."\n".v.description
  307. \ ,'dup': 1})
  308. endif
  309. endfor
  310. endf
  311. function! conn.invalidateSchema()
  312. let self['schema'] = {'tables' : {}}
  313. endfunction
  314. call conn['invalidateSchema']()
  315. function! conn.databases()
  316. return vim_addon_sql#MapIf(
  317. \ split(vim_addon_sql#System(self['cmd']+["-e",'show databases\G']),"\n"),
  318. \ "Val =~ '^Database: '", "matchstr(Val, ".string('Database: \zs.*').")")
  319. endfun
  320. " output must have been created with \G, no multilines supported yet
  321. function! conn.col(col, output)
  322. return vim_addon_sql#MapIf( split(a:output,"\n")
  323. \ , "Val =~ '^\\s*".a:col.": '", "matchstr(Val, ".string('^\s*'.a:col.': \zs.*').")")
  324. endfunction
  325. if !has_key(conn,'database')
  326. let conn['database'] = tlib#input#List("s" 'Select a mysql database', conn['databases']())
  327. endif
  328. function! conn.tables()
  329. " no caching yet
  330. return vim_addon_sql#MapIf(
  331. \ split(vim_addon_sql#System(self['cmd']+[self.database,"-e",'show tables\G']),"\n"),
  332. \ "Val =~ '^Tables_in[^:]*: '", "matchstr(Val, ".string('Tables_in[^:]*: \zs.*').")")
  333. endfun
  334. function! conn.loadFieldsOfTables(tables)
  335. for table in a:tables
  336. let r = self.query('describe `'.table.'`\G')
  337. let self['schema']['tables'][table] = { 'fields' : self.col('Field',r) }
  338. endfor
  339. endfunction
  340. function! conn.fields(table)
  341. if !exists('self["schema"]["tables"]['.string(a:table).']["fields"]')
  342. call self.loadFieldsOfTables([a:table])
  343. endif
  344. return self["schema"]["tables"][a:table]['fields']
  345. endfunction
  346. function! conn.query(sql)
  347. try
  348. return s:ClearError(vim_addon_sql#System(self['cmd']+[self['database']],{'stdin-text': a:sql}))
  349. catch /.*/
  350. call s:ShowError(v:exception)
  351. endtry
  352. endfun
  353. return conn
  354. endfunction
  355. fun! vim_addon_sql#ExtractMysqlFunctionsFromInfoFile(path)
  356. " mysql source contains Docs/mysql.info
  357. " Thus func extracts info about all functions
  358. exec 'sp '.fnameescape(a:path)
  359. normal gg
  360. let functions = {}
  361. while search('^ \* \`\S\+(','W')
  362. exec 'normal v/^ \* \`\S\+(\|^\S/-1'."\<cr>y"
  363. let c = split(@","\n")
  364. let header = ''
  365. while c[0] !~ '^\s*$'
  366. let header .= c[0]
  367. let c = c[1:]
  368. endwhile
  369. let header = substitute(header,'\s\+',' ','g')[4:]
  370. for i in split(header, "', \\`")
  371. let r = matchlist(i, '\([^(]\+\)(\([^)]*\)')
  372. if len(r) < 2
  373. echoe '>> '.string(i)
  374. endif
  375. let functions[r[1]] = {'args': r[2], 'description':join(c[1:],"\n")}
  376. endfor
  377. endwhile
  378. call writefile([string(functions)], s:mysqlFunctionsDump)
  379. endf
  380. " postgres implementation {{{1
  381. function! vim_addon_sql#PostgresConn(conn)
  382. let conn = a:conn
  383. let conn['regex'] = {
  384. \ 'table' :'\%(`[^`]\+`\|[^ \t\r\n,;`]\+\)'
  385. \ , 'table_from_match' :'^`\?\zs[^`\r\n]*\ze`\?$'
  386. \ }
  387. if ! has_key(conn,'cmd')
  388. let cmd=[s:c.psql]
  389. if has_key(conn, 'host')
  390. call add(cmd,'-h') | call add(cmd,conn['host'])
  391. endif
  392. if has_key(conn, 'port')
  393. call add(cmd,'-P') | call add(cmd,conn['port'])
  394. endif
  395. if has_key(conn, 'user')
  396. call add(cmd,'-U') | call add(cmd,conn['user'])
  397. endif
  398. " use a .pgpass file or define PGPASS ..
  399. "if has_key(conn, 'password')
  400. " call add(cmd,'--password='.conn['password'])
  401. "endif
  402. if has_key(conn, 'extra_args')
  403. call add(cmd,conn['extra_args'])
  404. endif
  405. let conn['cmd'] = cmd
  406. endif
  407. fun! conn.extraCompletions()
  408. " TODO add Postgresql function names
  409. if !exists('self["functions"]')
  410. let self['functions'] = self.parse(self.query('\df'))
  411. endif
  412. for line in self['functions']
  413. if s:Match(line[1])
  414. call complete_add({'word': line[1] .'('.join(map(split(line[3],', '),'"<+".v:val."+>"'),", ").')'
  415. \ ,'abbr': line[1]
  416. \ ,'menu': line[0].' '.line[2].' '.substitute(line[3],'\<\([^ ]\{0,2}\)[^, ]*','\1','g')
  417. \ ,'info': line[3]
  418. \ ,'dup': 1})
  419. endif
  420. endfor
  421. endf
  422. function! conn.invalidateSchema()
  423. let self['schema'] = {'tables' : {}}
  424. endfunction
  425. call conn['invalidateSchema']()
  426. " parse output of psql -l or echo '\d tablename' | psql
  427. " output must be a string
  428. function conn.parse(output)
  429. let lines = split(a:output, "\n")
  430. let idx = 0
  431. " ignore headlines
  432. while idx < len(lines) && lines[idx][:3] != "----"
  433. let idx = idx+1
  434. endwhile
  435. let idx = idx+1
  436. let result = []
  437. while idx < len(lines)
  438. if lines[idx][0] == '(' || lines[idx] =~ '^\s*$'
  439. " break when reaching num of lines
  440. break
  441. endif
  442. let cols = split(lines[idx],'|')
  443. call map(cols, 'matchstr(v:val, "^\\s*\\zs.\\{-}\\ze\\s*$")')
  444. call add(result, cols)
  445. let idx=idx+1
  446. endwhile
  447. return result
  448. endfun
  449. function! conn.databases()
  450. throw "conn.dabases not implemented for sqlite"
  451. endfun
  452. function! conn.tables()
  453. return map(self.parse(self.query('\dt')),'v:val[1]')
  454. endfun
  455. function! conn.loadFieldsOfTables(tables)
  456. if !exists('self["schema"]["tables"]['.string(a:table).']["fields"]')
  457. call self.loadFieldsOfTables([a:table])
  458. endif
  459. return self["schema"]["tables"][a:table]['fields']
  460. endfunction
  461. function! conn.fields(table)
  462. if !exists('self["schema"]["tables"]['.string(a:table).']["fields"]')
  463. call self.loadFieldsOfTables([a:table])
  464. endif
  465. return self["schema"]["tables"][a:table]['fields']
  466. endfunction
  467. function! conn.query(sql)
  468. try
  469. return s:ClearError(vim_addon_sql#System(self['cmd']+[self['database']],{'stdin-text': a:sql}))
  470. catch /.*/
  471. call s:ShowError(v:exception)
  472. endtry
  473. endfun
  474. return conn
  475. endfunction
  476. " sqlite implementation {{{1
  477. " conn must be {'database':'..','cmd':'sqlite3'}
  478. function! vim_addon_sql#SqliteConn(conn)
  479. let conn = a:conn
  480. let conn['executable'] = get(conn,'executable', 'sqlite3')
  481. let conn['regex'] = {
  482. \ 'table' :'\%(`[^`]\+`\|[^ \t\r\n,;`]\+\)'
  483. \ , 'table_from_match' :'^`\?\zs[^`\r\n]*\ze`\?$'
  484. \ }
  485. if ! has_key(conn, 'database')
  486. throw 'sqlite connection requires key database!'
  487. endif
  488. if ! has_key(conn,'cmd')
  489. let conn['cmd']=[s:c.sqlite3]
  490. endif
  491. fun! conn.extraCompletions()
  492. " TODO add sqlite function names etc
  493. return []
  494. endf
  495. function! conn.invalidateSchema()
  496. let self['schema'] = {'tables' : {}}
  497. endfunction
  498. call conn['invalidateSchema']()
  499. " function! conn.databases()
  500. " return vim_addon_sql#MapIf(
  501. " \ split(vim_addon_sql#System(self['cmd']+["-e",'show databases\G']),"\n"),
  502. " \ "Val =~ '^Database: '", "matchstr(Val, ".string('Database: \zs.*').")")
  503. " endfun
  504. " output must have been created with \G, no multilines supported yet
  505. function! conn.col(col, output)
  506. return vim_addon_sql#MapIf( split(a:output,"\n")
  507. \ , "Val =~ '^\\s*".a:col.": '", "matchstr(Val, ".string('^\s*'.a:col.': \zs.*').")")
  508. endfunction
  509. function! conn.tables()
  510. " no caching yet
  511. " TODO: handle spaces and quoting ?
  512. return split(self.query('.tables'),'[ \t\r\n]\+')
  513. endfun
  514. function! conn.loadFieldsOfTables(tables)
  515. for table in a:tables
  516. let fields = []
  517. let lines = split(self.query('.schema '.table),"\n")
  518. for l in lines
  519. if l =~ 'CREATE TABLE' | continue | endif
  520. " endo of field list
  521. if l =~ ');' | break | endif
  522. call add(fields, matchstr( l, '^[ \t]*\zs\S*\ze') )
  523. endfor
  524. let self['schema']['tables'][table] = { 'fields' : fields }
  525. endfor
  526. endfunction
  527. function! conn.fields(table)
  528. if !exists('self["schema"]["tables"]['.string(a:table).']["fields"]')
  529. call self.loadFieldsOfTables([a:table])
  530. endif
  531. return self["schema"]["tables"][a:table]['fields']
  532. endfunction
  533. function! conn.query(sql)
  534. try
  535. return s:ClearError(vim_addon_sql#System(self['cmd']+[self['database']],{'stdin-text': a:sql}))
  536. catch /.*/
  537. call s:ShowError(v:exception)
  538. endtry
  539. endfun
  540. return conn
  541. endfunction
  542. " vim:fdm=marker
  543. " firebird implementation {{{1
  544. " conn must be {'database:'..', ['user':'..','password':.., 'cmd':'isql']}
  545. " TODO: also provide completion for triggers ...
  546. function! vim_addon_sql#FirebirdConn(conn)
  547. let conn = a:conn
  548. let conn['executable'] = get(conn,'executable', s:c.isql)
  549. let conn['connect_by_args'] = 1
  550. " also allow key user because its the name being used in isql commands
  551. let conn['username'] = get(conn,'username', get(conn, 'user','SYSDBA'))
  552. let conn['password'] = get(conn,'password','masterkey')
  553. let conn['fields_ignore_case'] = 1
  554. let conn['regex'] = {
  555. \ 'table' :'\%(`[^`]\+`\|[^ \t\r\n,;`]\+\)'
  556. \ , 'table_from_match' :'^`\?\zs[^`\r\n]*\ze`\?$'
  557. \ }
  558. if ! has_key(conn,'database')
  559. throw 'firebird connection requires key database!'
  560. endif
  561. let conn['cmd'] = [conn.executable]
  562. fun! conn.extraCompletions()
  563. " TODO add firebird function names etc
  564. return []
  565. endf
  566. function conn.create()
  567. let command = "CREATE DATABASE '". self.database ."' page_size 8192\n"
  568. \ ."user '". self.username ."' password '". self.password ."';"
  569. call vim_addon_sql#System([self['executable']],{'stdin-text': command.';;'})
  570. endfunction
  571. function! conn.invalidateSchema()
  572. let self['schema'] = {'tables' : {}}
  573. endfunction
  574. call conn['invalidateSchema']()
  575. function! conn.col(col, output)
  576. return vim_addon_sql#MapIf( split(a:output,"\n")
  577. \ , "Val =~ '^\\s*".a:col.": '", "matchstr(Val, ".string('^\s*'.a:col.': \zs.*').")")
  578. endfunction
  579. function! conn.tables()
  580. " no caching yet
  581. " TODO: handle spaces and quoting ?
  582. return split(self.query('show tables'),'[ \t\r\n]\+')
  583. endfun
  584. function! conn.loadFieldsOfTables(tables)
  585. for table in a:tables
  586. let fields = []
  587. let lines = split(self.query('show table '.table),"\n")
  588. for l in lines
  589. " : denotes trigger or constraints section
  590. if l =~ ':$' | break | endif
  591. call add(fields, matchstr( l, '^\zs\S*\ze') )
  592. endfor
  593. let self['schema']['tables'][table] = { 'fields' : fields }
  594. endfor
  595. endfunction
  596. function! conn.fields(table)
  597. if !exists('self["schema"]["tables"]['.string(a:table).']["fields"]')
  598. call self.loadFieldsOfTables([a:table])
  599. endif
  600. return self["schema"]["tables"][a:table]['fields']
  601. endfunction
  602. function! conn.query(sql)
  603. " don't append \n so that line numbers fit
  604. let sql = ''
  605. try
  606. if self.connect_by_args
  607. let cmd = copy(self['cmd'])
  608. if has_key(self, 'username')
  609. let cmd += ['-u', self.username, '-pass', self.password]
  610. endif
  611. let cmd += ["-pag", "0"]
  612. let cmd += [self.database]
  613. return s:ClearError(vim_addon_sql#System(cmd,{'stdin-text': a:sql.';'}))
  614. else
  615. let authorize = "CONNECT '".self['database']."' ".(has_key(self, 'username') ? "USER '".self['username']."' PASSWORD '".self['password']."'" : '').";\n"
  616. return s:ClearError(vim_addon_sql#System(self['cmd'],{'stdin-text': authorize . a:sql .';'}))
  617. endif
  618. catch /.*/
  619. call s:ShowError(v:exception)
  620. endtry
  621. endfun
  622. return conn
  623. endfunction
  624. "echo c.query('CREATE DATABASE')
  625. " vim:fdm=marker
  626. "