From 5c1c42573e8cc481bf7edb0e06c7729bedd65de9 Mon Sep 17 00:00:00 2001 From: Cosmin Popescu Date: Thu, 20 Aug 2015 21:40:48 +0200 Subject: [PATCH] version 4.0 --- README.md | 63 ++++++++++++-- autoload/sw.vim | 48 +++++++++-- autoload/sw/autocomplete.vim | 5 +- autoload/sw/dbexplorer.vim | 54 +++++++++--- autoload/sw/server.vim | 20 +++-- autoload/sw/sqlwindow.vim | 21 +++-- autoload/sw/variables.vim | 17 +--- resources/dbexplorer-postgresql.vim | 19 ----- resources/dbexplorer.vim | 41 +++++++--- resources/sqlwbserver | 123 ++++++++++++++++++---------- 10 files changed, 282 insertions(+), 129 deletions(-) delete mode 100644 resources/dbexplorer-postgresql.vim diff --git a/README.md b/README.md index 4329d18..e70bc9c 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,10 @@ listen for requests. After this, whenever you want to send a command to the DBMS from `VIM`, the plugin will connect on the specified port, send the command and retrieve the result which will be displayed in `VIM`. +In order to work properly, you should keep the settings on default. Settings +like `workbench.console.script.showtime` could affect the functionality of +`VIM sql workbench`. + Connecting to a DBMS ======================================== @@ -158,13 +162,6 @@ available shortcuts, see the top panel. The database explorer if fully customizable. You can use the existing one and extend it or you can create your own from scratch. -_NOTE_: For `PostgreSQL`, you should use the as database explorer the -`resources/dbexplorer-postgresql.vim` file. This is because in `PostgreSQL` -the objects have to be prefixed by the schema. You can achieve this either by -just overwriting the `resources/dbexplorer.vim` file with the -`resources/dbexplorer-postgresql.vim` file, either by following the -documentation bellow. - ## Creating a new database explorer from scratch The database explorer is loaded from the `resources/dbexplorer.vim` file by @@ -179,8 +176,41 @@ which the panel will be applied. `*` profile, means that the options appear on all profiles. If you want to have separate database explorers for separate profiles, you can create a key in the dictionary for each explorer. -*NOTE:* At the moment you can only create profiles for different profiles, not -for different DBMS. +You can also have profiles per type of DBMS. If you have a profile starting +with a `:` or a '^'. + +A `:` means that this options will appear for all the profiles which the DBMS +is of that type. For example `:MySQL` it means that these options will appear +only for `mysql` databases. + +A `^` means that this options will appear for all the profiles for which the +DBMS is not of that type. For example `^PostgreSQL` means that there options +will appear for all databases which are not `PostgreSQL`. + +For this to work, you have to have the option `g:sw_config_dir` set. The +profile informations are read from the `WbProfiles.xml` file which resides in +this folder. The profile type you can see it in the `SQL Workbench/J` +connection window. It's the driver title. + +Starting with version `4.0` you can also have a vimscript function called +instead of a sql query. The function called has to return a string which will +be interpreted as the result of the operation. The function will receive as +parameters the line selected (the complete line which has been selected). In +order to have a function instead of a sql query in the database explorer, the +`command` has to begin with `:`. + +For example: + +``` +{'title': 'Data', 'shortcut': 'D', 'command': ':My_function'} +``` + +When the shortcut D will be pressed, the result will be fetch by calling +`My_function(getline('.'))` + +Of course, the current line is only relevant only for when changing a tab. +When changing a tab, the current line will contain whatever value is on the +currently line in whatever buffer you are at that moment. The values for each profile, have to be a list which will contain all the options for the left panel. For example, in the default one, the database @@ -376,6 +406,21 @@ is going to be sent to the DBMS. Also here you can use an exclamation mark to execute the command asynchronous, which is the default mapping. +## Profiling + +Unfortunately, the `SQL Workbench/J` console application does not return the +time that it took for a command to execute. This plugin will try to do some +profiling, but it will report the full time it took for a command to execute. +This means that this time will also include the communication with the +`sqwbconsole` server, the time to display the results in console (if on debug +mode) the time it took `SQL Workbench/J` console application to communicate +with the DBMS via `jdbc` and any other operations involved. + +So, if you want to do some profiling, try to either to `select count(*) from +your_table` (this would eliminate some operations, like displaying the results +in console if in debug mode) or to set the maximum number of results to a low +value (like 10). And (of course), send only one query at a time. + ## Intellisense `vim-sqlworkbench` plugin comes with intellisense out of the box. In order to diff --git a/autoload/sw.vim b/autoload/sw.vim index d81ad7f..45811d6 100644 --- a/autoload/sw.vim +++ b/autoload/sw.vim @@ -366,7 +366,7 @@ endfunction " Parses the profile xml file to give autocompletion for profiles{{{1 function! sw#parse_profile_xml() if !exists('g:sw_config_dir') - return [] + return {} endif let lines = readfile(g:sw_config_dir . 'WbProfiles.xml') @@ -375,16 +375,25 @@ function! sw#parse_profile_xml() let s = s . ' ' . line endfor - let pattern = '\v\c\.{-}\.{-}\([^\<]{-})\<' - let result = [] - let n = 1 - let list = matchlist(s, pattern, 0, n) + let s = substitute(s, '\v\c\', '', 'g') + let s = substitute(s, '\v\c\.{-}\<\/object\>', '', 'g') + + let pattern = '\v\c(\.{-}\<\/object\>)' + let result = {} + let n = 0 + let list = matchlist(s, pattern, n, 1) while len(list) > 0 - if index(result, list[1]) == -1 - call add(result, list[1]) + let _pattern = '\v\c^.*\[ \s\r\t]*\([^\<]+)\<.*$' + let name = substitute(list[1], substitute(_pattern, '#prop#', 'name', 'g'), '\1', 'g') + let driverName = substitute(list[1], substitute(_pattern, '#prop#', 'driverName', 'g'), '\1', 'g') + let group = substitute(list[1], substitute(_pattern, '#prop#', 'group', 'g'), '\1', 'g') + if (group != list[1]) + let name = group . '\' . name endif + let result[name] = driverName let n = n + 1 - let list = matchlist(s, pattern, 0, n) + let s = substitute(s, '\V' . list[0], '', 'g') + let list = matchlist(s, pattern, n, 1) endwhile return result @@ -395,7 +404,7 @@ function! sw#autocomplete_profile(ArgLead, CmdLine, CursorPos) let result = [] - for profile in profiles + for profile in keys(profiles) if profile =~ '^' . a:ArgLead call add(result, profile) endif @@ -431,3 +440,24 @@ function! sw#autocomplete_profile_for_buffer(ArgLead, CmdLine, CursorPos) endif return s:autocomplete_path(a:ArgLead, a:CmdLine, a:CursorPos) endfunction + +function! sw#display_error(msg) + echohl WarningMsg + echomsg a:msg + echohl None +endfunction + +function! sw#get_sw_setting(setting) + let p1 = '\v\c^[\s \t]*' . substitute(a:setting, '\c\v\.', "\\.", 'g') + if exists('g:sw_config_dir') + let lines = readfile(g:sw_config_dir . 'workbench.settings') + for line in lines + if line =~ p1 + let p2 = p1 . '[\s \t]*\=[\s\t ]*(.*)$' + return substitute(line, p2, '\1', 'g') + endif + endfor + endif + + return '' +endfunction diff --git a/autoload/sw/autocomplete.vim b/autoload/sw/autocomplete.vim index 11f1a4a..4559b12 100644 --- a/autoload/sw/autocomplete.vim +++ b/autoload/sw/autocomplete.vim @@ -299,7 +299,8 @@ endfunction function! sw#autocomplete#perform(findstart, base) " Check that the cache is alright if !exists('b:autocomplete_tables') && !exists('g:sw_autocomplete_default_tables') - throw "First you have to clear the completion cache to use autocomplete" + call sw#display_error("First you have to clear the completion cache to use autocomplete") + return [] endif call sw#session#init_section() if (exists('b:sql')) @@ -473,7 +474,7 @@ function! sw#autocomplete#perform(findstart, base) elseif b:autocomplete_type == 'wbconnect' let profiles = sw#parse_profile_xml() let result = [] - for profile in profiles + for profile in keys(profiles) if profile =~ '^' . a:base call add(result, profile) endif diff --git a/autoload/sw/dbexplorer.vim b/autoload/sw/dbexplorer.vim index c98d098..a378f81 100644 --- a/autoload/sw/dbexplorer.vim +++ b/autoload/sw/dbexplorer.vim @@ -21,6 +21,8 @@ if !exists('g:SW_Tabs') let g:SW_Tabs = {} endif +let s:profiles = {} + " Local functions{{{1 " Iterates in the tabs array{{{2 @@ -28,11 +30,28 @@ function! s:iterate(f) for _profile in items(g:SW_Tabs) let profile = _profile[0] let tabs = _profile[1] - if (profile == b:profile || profile == '*') + let type_ok = 0 + if (profile =~ '^[:\^]') + if (len(s:profiles) == 0) + let s:profiles = sw#parse_profile_xml() + endif + let dbms_type = substitute(profile, '^[:\^]', '', 'g') + let cond = substitute(profile, '\v\c^([:\^]).*$', '\1', 'g') + + for xml_profile in items(s:profiles) + if tolower(substitute(xml_profile[0], "\\\\", '___', 'g')) == tolower(b:profile) && + \ ((tolower(xml_profile[1]) == tolower(dbms_type) && cond == ':') || + \ (tolower(xml_profile[1]) != tolower(dbms_type) && cond == '^')) + let type_ok = 1 + break + endif + endfor + endif + if (profile == b:profile || profile == '*' || type_ok) for tab in tabs execute "let r = " . a:f . "(tab)" if !r - break + return endif endfor endif @@ -358,7 +377,7 @@ function! sw#dbexplorer#hide_panel(...) endif let name = "__SQL__-" . profile if !bufexists(name) - echoerr "There is no dbexplorer opened for " . profile + call sw#display_error("There is no dbexplorer opened for " . profile) return endif @@ -372,7 +391,7 @@ function! sw#dbexplorer#export() if (exists('b:last_cmd')) call sw#export_ods(b:profile, b:last_cmd) else - echoerr "The panel is empty!" + call sw#display_error("The panel is empty!") endif endfunction @@ -423,12 +442,13 @@ endfunction " Shows the dbexplorer panel{{{2 function! sw#dbexplorer#show_panel(profile, port, ...) - let result = sw#server#open_dbexplorer(a:profile, a:port) + let profile = substitute(a:profile, '\\', '___', 'g') + let result = sw#server#open_dbexplorer(profile, a:port) let s_below = &splitbelow set nosplitbelow - let name = "__SQL__-" . a:profile + let name = "__SQL__-" . profile if bufexists(name) - echoerr "There is already a dbexplorer opened for " . a:profile + call sw#display_error("There is already a dbexplorer opened for " . profile) return endif @@ -447,19 +467,19 @@ function! sw#dbexplorer#show_panel(profile, port, ...) execute "badd " . name execute "buffer " . name - call s:set_special_buffer(a:profile, a:port) + call s:set_special_buffer(profile, a:port) call sw#session#set_buffer_variable('unique_id', uid) nnoremap E :call sw#dbexplorer#export() nnoremap B :call open_in_new_buffer() - execute "silent! split __Info__-" . a:profile + execute "silent! split __Info__-" . profile resize 7 "let id = matchadd('SWHighlights', '\v^([^\(]+\([A-Za-z]+\)( \| )?)+$') - call s:set_special_buffer(a:profile, a:port) + call s:set_special_buffer(profile, a:port) call sw#session#set_buffer_variable('unique_id', uid) call s:set_highlights() wincmd b - execute "silent! vsplit __DBExplorer__-" . a:profile - call s:set_special_buffer(a:profile, a:port) + execute "silent! vsplit __DBExplorer__-" . profile + call s:set_special_buffer(profile, a:port) call sw#session#set_buffer_variable('unique_id', uid) vertical resize 60 ""call s:set_objects_buffer() @@ -482,4 +502,14 @@ function! sw#dbexplorer#fix_source_code() normal gg0G0x setlocal nomodifiable endfunction + +function! sw#dbexplorer#postgre_proc(line) + let proc = substitute(a:line, '\v\c([^\(]+)\(.*$', '\1', 'g') + let lines = sw#server#dbexplorer('WbProcSource ' . proc) + let result = '' + for line in lines + let result = result . line . "\n" + endfor + return result +endfunction " vim:fdm=marker diff --git a/autoload/sw/server.vim b/autoload/sw/server.vim index c211c67..e0685ff 100644 --- a/autoload/sw/server.vim +++ b/autoload/sw/server.vim @@ -89,6 +89,7 @@ function! s:pipe_execute(type, cmd, wait_result, ...) let uid = b:unique_id endif + try python << SCRIPT import vim import socket @@ -127,6 +128,10 @@ for line in lines: vim.command("let result = result . '%s\n'" % line.replace("'", "''")) #end for SCRIPT + catch + call sw#display_error("There is a problem communicating with the server on port " . port . ". Maybe the server is down?") + return '' + endtry if len(result) <= 3 let result = '' endif @@ -143,17 +148,22 @@ function! sw#server#fetch_result() endfunction function! sw#server#open_dbexplorer(profile, port) - return s:pipe_execute('DBE', a:profile . "\n", 1, a:port) + return s:pipe_execute('DBE', substitute(a:profile, '___', "\\\\", 'g') . "\n", 1, a:port) endfunction function! sw#server#dbexplorer(sql) if !exists('b:profile') return endif - let s = s:pipe_execute('DBE', b:profile . "\n" . a:sql . ';', 1) + if a:sql =~ "^:" + let func = substitute(a:sql, '^:', '', 'g') + execute "let s = " . func . "(getline('.'))" + else + let s = s:pipe_execute('DBE', substitute(b:profile, '___', "\\\\", 'g') . "\n" . a:sql . ';', 1) + endif let lines = split(s, "\n") let result = [] - let rec = 0 + let rec = 1 for line in lines if line =~ '\v\c^[ \s\t\r]*$' let rec = 0 @@ -161,7 +171,7 @@ function! sw#server#dbexplorer(sql) call add(result, '') endif endif - if rec + if rec && !(line =~ '\v^[\=]+$') call add(result, line) endif if line =~ '\v\c^[\=]+$' @@ -176,7 +186,7 @@ endfunction function! sw#server#execute_sql(sql, wait_result, port) let sql = a:sql - if !(substitute(sql, "^\\v\\c\\n", ' ', 'g') =~ b:delimiter . '[ \s\t\r]*$') + if !(substitute(sql, "^\\v\\c\\n", ' ', 'g') =~ b:delimiter . '[ \s\t\r\n]*$') let sql = sql . b:delimiter . "\n" endif return s:pipe_execute('COM', sql, a:wait_result, a:port) diff --git a/autoload/sw/sqlwindow.vim b/autoload/sw/sqlwindow.vim index f4dcb70..c57e9a7 100644 --- a/autoload/sw/sqlwindow.vim +++ b/autoload/sw/sqlwindow.vim @@ -23,8 +23,10 @@ let s:script_path = expand(':p:h') . '/../../' function! s:check_sql_buffer() if (!exists('b:port')) - throw "The current buffer is not an SQL Workbench buffer. Open it using the SWOpenSQL command." + call sw#display_error("The current buffer is not an SQL Workbench buffer. Open it using the SWOpenSQL command.") + return 0 endif + return 1 endfunction function! sw#sqlwindow#goto_statement_buffer() @@ -115,9 +117,12 @@ function! sw#sqlwindow#open_buffer(port, file, command) endfunction function! sw#sqlwindow#set_delimiter(new_del) - call s:check_sql_buffer() + if (!s:check_sql_buffer()) + return + endif if exists('b:port') - throw 'You cannot change the delimier in server mode. This happens because SQL Workbench does now know another delimiter during console mode. You can only change the delimiter in batch mode (see the documentation). So, if you want to change the delimiter, please open the buffer in batch mode. ' + sw#display_error('You cannot change the delimier in server mode. This happens because SQL Workbench does now know another delimiter during console mode. You can only change the delimiter in batch mode (see the documentation). So, if you want to change the delimiter, please open the buffer in batch mode.') + return endif call sw#session#set_buffer_variable('delimiter', a:new_del) endfunction @@ -144,6 +149,10 @@ function! sw#sqlwindow#extract_current_sql(...) let s = s . line . "\n" endfor + if !exists('b:delimiter') + call sw#display_error("The buffer is not connected to a server. Please use SWSqlConectToServer before running queries") + return '' + endif let sqls = sw#sql_split(s, b:delimiter) for sql in sqls if sql =~ '#CURSOR#' @@ -153,7 +162,7 @@ function! sw#sqlwindow#extract_current_sql(...) return sql endif endfor - throw "Could not identifiy the current query" + call sw#display_error("Could not identifiy the current query") return "" endfunction @@ -459,7 +468,9 @@ function! sw#sqlwindow#execute_sql(wait_result, sql) let w:auto_added1 = "-- auto\n" let w:auto_added2 = "-- end auto\n" - call s:check_sql_buffer() + if (!s:check_sql_buffer()) + return + endif let _sql = a:sql if !exists('b:no_variables') let vars = sw#variables#extract(_sql) diff --git a/autoload/sw/variables.vim b/autoload/sw/variables.vim index d8c66f8..782be3c 100644 --- a/autoload/sw/variables.vim +++ b/autoload/sw/variables.vim @@ -19,21 +19,12 @@ function! s:set_delimiters() if !exists('g:sw_p_suffix') && !exists('g:sw_p_prefix') - let p1 = '\v\c^[\s \t]*workbench\.sql\.parameter\.(suffix|prefix)' - if exists('g:sw_config_dir') - let lines = readfile(g:sw_config_dir . 'workbench.settings') - for line in lines - if line =~ p1 - let p2 = p1 . '[\s \t]*\=[\s\t ]*(.*)$' - let type = substitute(line, p2, '\1', 'g') - execute "let g:sw_p_" . type . " = substitute(line, p2, '\\2', 'g')" - endif - endfor - endif - if !exists('g:sw_p_prefix') + let g:sw_prefix = sw#get_sw_setting('workbench.sql.parameter.prefix') + let g:sw_suffix = sw#get_sw_setting('workbench.sql.parameter.suffix') + if g:sw_prefix == '' let g:sw_p_prefix = '\$\[' endif - if !exists('g:sw_p_suffix') + if g:sw_suffix == '' let g:sw_p_suffix = '\]' endif diff --git a/resources/dbexplorer-postgresql.vim b/resources/dbexplorer-postgresql.vim deleted file mode 100644 index 3f8a821..0000000 --- a/resources/dbexplorer-postgresql.vim +++ /dev/null @@ -1,19 +0,0 @@ -let sw_columns = {'title': 'Columns', 'shortcut': 'C', 'command': 'desc %3%.%0%'} - -let sw_sql_source = {'title': 'SQL Source', 'shortcut': 'S', 'command': 'WbGrepSource -searchValues="%object%" -objects=%object% -types=* -useRegex=true -schemas=*; -- AFTERcall sw#dbexplorer#fix_source_code()', 'skip_columns': [0, 1], 'hide_header': 1, 'filetype': 'sql'} - -let sw_data = {'title': 'Data', 'shortcut': 'D', 'command': 'select * from %3%.%0%'} - -let sw_indexes = {'title': 'Indexes', 'shortcut': 'I', 'command': 'WbListIndexes -tableName=%object% -schema=%3%'} - -let sw_referenced_by = {'title': 'Referenced by', 'shortcut': 'R', 'command': 'WbGrepSource -searchValues="references %object%" -types=TABLE -useRegex=false -schemas=*;', 'skip_columns': [2]} - -let objects = {'title': 'Objects', 'shortcut': 'O', 'command': 'WbList -objects=% -types=SYNONYM,SEQUENCE,TABLE,TYPE,VIEW', 'panels': [sw_columns, sw_sql_source, sw_data, sw_indexes, sw_referenced_by]} - -let procedures = {'title': 'Procedures', 'shortcut': 'P', 'command': 'WbListProcs;', 'panels': [sw_sql_source]} - -let triggers = {'title': 'Triggers', 'shortcut': 'T', 'command': 'WbListTriggers;', 'panels': [sw_sql_source]} - -let g:SW_Tabs = {'*': [objects, procedures, triggers]} - - diff --git a/resources/dbexplorer.vim b/resources/dbexplorer.vim index 2e0e3b7..3a88d5d 100644 --- a/resources/dbexplorer.vim +++ b/resources/dbexplorer.vim @@ -1,19 +1,36 @@ -let sw_columns = {'title': 'Columns', 'shortcut': 'C', 'command': 'desc %object%;'} +" General panel (for all DBMS) with the exception of PostgreSQL -let sw_sql_source = {'title': 'SQL Source', 'shortcut': 'S', 'command': 'WbGrepSource -searchValues="%object%" -objects=%object% -types=* -useRegex=true; -- AFTERcall sw#dbexplorer#fix_source_code()', 'skip_columns': [0, 1], 'hide_header': 1, 'filetype': 'sql'} +let row_counts = {'title': 'Row Counts', 'shortcut': 'W', 'command': 'WbRowCount;', 'panels': []} +let sw_columns = {'title': 'Columns', 'shortcut': 'C', 'command': 'desc %object%;'} +let sw_sql_source = {'title': 'SQL Source', 'shortcut': 'S', 'command': 'WbGenerateScript -objects="%object%"', 'filetype': 'sql'} +let sw_sql_source_triggers = {'title': 'SQL Source', 'shortcut': 'S', 'command': 'WbGrepSource -searchValues="%object%" -objects=%object% -types=* -useRegex=true; -- AFTERcall sw#dbexplorer#fix_source_code()', 'filetype': 'sql', 'skip_columns': [0, 1], 'hide_header': 1} let sw_data = {'title': 'Data', 'shortcut': 'D', 'command': 'select * from %object%;'} - let sw_indexes = {'title': 'Indexes', 'shortcut': 'I', 'command': 'WbListIndexes -tableName=%object%;'} - let sw_referenced_by = {'title': 'Referenced by', 'shortcut': 'R', 'command': 'WbGrepSource -searchValues="references %object%" -types=TABLE -useRegex=false;', 'skip_columns': [2]} - let objects = {'title': 'Objects', 'shortcut': 'O', 'command': 'WbList -objects=% -types=SYNONYM,SEQUENCE,TABLE,TYPE,VIEW', 'panels': [sw_columns, sw_sql_source, sw_data, sw_indexes, sw_referenced_by]} - +let sw_sql_source = {'title': 'SQL Source', 'shortcut': 'S', 'command': 'WbProcSource %object%', 'filetype': 'sql'} let procedures = {'title': 'Procedures', 'shortcut': 'P', 'command': 'WbListProcs;', 'panels': [sw_sql_source]} - -let triggers = {'title': 'Triggers', 'shortcut': 'T', 'command': 'WbListTriggers;', 'panels': [sw_sql_source]} - -let g:SW_Tabs = {'*': [objects, procedures, triggers]} - - +let triggers = {'title': 'Triggers', 'shortcut': 'T', 'command': 'WbListTriggers;', 'panels': [sw_sql_source_triggers]} +let g:SW_Tabs = {'^postgresql': [objects, procedures, triggers, row_counts]} + +" PostgreSQL panel +let sw_sql_source = {'title': 'SQL Source', 'shortcut': 'S', 'command': 'WbGenerateScript -objects="%object%"', 'filetype': 'sql'} +let sw_columns = {'title': 'Columns', 'shortcut': 'C', 'command': 'desc %3%.%0%'} +let sw_data = {'title': 'Data', 'shortcut': 'D', 'command': 'select * from %3%.%0%'} +let sw_indexes = {'title': 'Indexes', 'shortcut': 'I', 'command': 'WbListIndexes -tableName=%object% -schema=%3%'} +let sw_referenced_by = {'title': 'Referenced by', 'shortcut': 'R', 'command': 'WbGrepSource -searchValues="references %object%" -types=TABLE -useRegex=false -schemas=*;', 'skip_columns': [2]} +let objects = {'title': 'Objects', 'shortcut': 'O', 'command': 'WbList', 'panels': [sw_columns, sw_sql_source, sw_data, sw_indexes, sw_referenced_by]} +let sw_sql_source_proc = {'title': 'SQL Source', 'shortcut': 'S', 'command': ':sw#dbexplorer#postgre_proc', 'filetype': 'sql'} +let sw_sql_source_triggers = {'title': 'SQL Source', 'shortcut': 'S', 'command': 'WbGrepSource -searchValues="%object%" -objects=%object% -types=* -useRegex=true -schemas=*; -- AFTERcall sw#dbexplorer#fix_source_code()', 'skip_columns': [0, 1], 'hide_header': 1, 'filetype': 'sql'} +let procedures = {'title': 'Procedures', 'shortcut': 'P', 'command': 'WbListProcs;', 'panels': [sw_sql_source_proc]} +let triggers = {'title': 'Triggers', 'shortcut': 'T', 'command': 'WbListTriggers;', 'panels': [sw_sql_source_triggers]} +let schemas = {'title': 'Show schemas', 'shortcut': 'M', 'command': 'wblistschemas;', 'panels': [{'title': 'Select schema', 'shortcut': 'H', 'command': "set search_path to %object%;"}, {'title': 'Select *', 'shortcut': 'A', 'command': "set search_path to '*.*'"}]} +let g:SW_Tabs[':postgresql'] = [objects, procedures, triggers, schemas, row_counts] + +" Oracle specific panel +let oracle_dblinks = {'title': 'DB Links', 'shortcut': 'L', 'command': 'select db_link, username, created from user_db_links;', 'panels': [{'title': 'Show the host', 'shortcut': 'H', 'command': "select host from user_db_links where db_link = '%object%'"}]} +let oracle_jobs = {'title': 'User Jobs', 'shortcut': 'J', 'command': 'select job_name, job_creator, start_date, repeat_interval from user_scheduler_jobs', 'panels': [{'title': 'Job source', 'shortcut': 'S', 'command': "select job_action from user_scheduler_jobs where job_name = '%object%'", 'hide_header': 1, 'filetype': 'sql'}]} +let oracle_packages = {'title': 'Packages', 'shortcut': 'K', 'command': "select OBJECT_NAME, OBJECT_TYPE, STATUS from user_objects where object_type in ('PACKAGE');", 'panels': [{'title': 'SQL Source', 'shortcut': 'S', 'command': "wbprocsource %object%", 'filetype': 'sql'}, {'title': 'Compile', 'shortcut': 'E', 'command': 'alter package %object% compile package'}]} +let oracle_schemas = {'title': 'Show schemas', 'shortcut': 'M', 'command': 'wblistschemas;', 'panels': [{'title': 'Select schema', 'shortcut': 'H', 'command': "alter session set current_schema = %object%;"}]} +let g:SW_Tabs[':oracle'] = [oracle_dblinks, oracle_jobs, oracle_packages, oracle_schemas] diff --git a/resources/sqlwbserver b/resources/sqlwbserver index 6b48fdb..3c25e92 100755 --- a/resources/sqlwbserver +++ b/resources/sqlwbserver @@ -22,16 +22,18 @@ class SQLWorkbench(object): port = 5000 results = {} debug = 0 + colwidth = 120 threads_started = 0 vim = 'vim' tmp = "/tmp" + clock = datetime.datetime.now() quit = False lock = thread.allocate_lock() executing = thread.allocate_lock() - processing = thread.allocate_lock() new_loop = thread.allocate_lock() prompt_pattern_begin = '^[a-zA-Z_0-9\\.]+(\\@[a-zA-Z_0-9/]+)?\\>[ \s\t]*' prompt_pattern = prompt_pattern_begin + '$' + resultset_end_pattern = 'send_to_vim set to' buff = '' dbe_connections = {} identifier = None @@ -53,11 +55,13 @@ class SQLWorkbench(object): def parseCustomCommand(self, command): if command[0] == 'identifier': self.identifier = command[1] + elif command[0] == 'colwidth': + self.colwidth = command[1] #end if #end def parseCustomCommand def gotCustomCommand(self, text): - pattern = '^!#(identifier|end)[ \\s\\t]*=[ \\s\\t]*([A-Za-z_0-9#]+)[ \\s\\t\\n\\r]*$' + pattern = '^!#(identifier|end|colwidth)[ \\s\\t]*=[ \\s\\t]*([A-Za-z_0-9#]+)[ \\s\\t\\n\\r]*$' p = re.search(pattern, text) if p != None: return [p.group(1), p.group(2)] @@ -145,7 +149,12 @@ class SQLWorkbench(object): def spawnDbeConnection(self, profile, conn): self.startThread() - cmd = "%s -feedback=true -showProgress=false -profile=%s" % (self.cmd, profile) + pattern = '^([^\\\\]+)\\\\(.*)$' + _p = profile + if re.match(pattern, profile) != None: + _p = re.sub(pattern, '\\2', profile) + " -profileGroup=" + re.sub(pattern, '\\1', profile) + #end if + cmd = "%s -feedback=true -showProgress=false -profile=%s" % (self.cmd, _p) pipe = subprocess.Popen(shlex.split(cmd), stdin = subprocess.PIPE, stdout = subprocess.PIPE, bufsize = 1) pipe.stdin.write('set maxrows = 100;\n') conn.send('DISCONNECT') @@ -181,7 +190,10 @@ class SQLWorkbench(object): pipe = self.dbe_connections[profile] data = conn.recv(4096) if (data): - data += "\nwbvardef send_to_vim = 1;\nwbvardelete send_to_vim;\n" + data += "\nwbsetconfig send_to_vim=1;\n" + if self.debug: + print "SEND TO SERVER: " + data + #end if pipe.stdin.write(data) result = self.receiverDbe(pipe) conn.send(self.prepareResult(result)) @@ -219,44 +231,46 @@ class SQLWorkbench(object): #end def readData def receiveData(self, conn, pipe, n): - with self.processing: - self.identifier = None - buff = self.readData(conn, n) - lines = buff.split("\n") - for line in lines: - if re.search('^!#', line) != None: - command = self.gotCustomCommand(line) - if command != None: - self.parseCustomCommand(command) - #end if - else: + self.clock = datetime.datetime.now() + self.identifier = None + buff = self.readData(conn, n) + lines = buff.split("\n") + for line in lines: + if re.search('^!#', line) != None: + command = self.gotCustomCommand(line) + if command != None: + self.parseCustomCommand(command) + #end if + else: + if self.debug: + print "SENT TO SERVER: " + line + #end if + if line != '': + pipe.stdin.write(line + "\n") + #end if + #end if + #end for + with self.new_loop: + pipe.stdin.write("wbsetconfig send_to_vim=1;\n") + if self.debug: + print "SENT TO SERVER: wbsetconfig send_to_vim=1;" + #end if + if self.identifier == None: + with self.executing: + data = self.prepareResult(self.buff) + conn.send(data) + #end with + else: + with self.executing: + p = self.getCaller() if self.debug: - print "SENT TO SERVER: " + line + print "RESULT STORED FOR %s" % p.group(1) + "#" + p.group(2) #end if - if line != '': - pipe.stdin.write(line + "\n") + if p != None: + self.toVim('sw#got_async_result(\\"%s\\")' % p.group(2)) #end if - #end if - #end for - with self.new_loop: - pipe.stdin.write("wbvardef send_to_vim = 1;\nwbvardelete send_to_vim;\n") - if self.identifier == None: - with self.executing: - data = self.prepareResult(self.buff) - conn.send(data) - #end with - else: - with self.executing: - p = self.getCaller() - if self.debug: - print "RESULT STORED FOR %s" % p.group(1) + "#" + p.group(2) - #end if - if p != None: - self.toVim('sw#got_async_result(\\"%s\\")' % p.group(2)) - #end if - #end with - #end if - #end with + #end with + #end if #end with #end def receiveData @@ -280,7 +294,7 @@ class SQLWorkbench(object): elif data == 'RES': self.searchResult(conn, n - 3) elif data == 'DBE': - self.dbExplorer(conn, n) + self.dbExplorer(conn, n - 3) #end if conn.close() #end def newConnection @@ -328,7 +342,7 @@ class SQLWorkbench(object): def receiverDbe(self, pipe): line = '' buff = '' - while re.search('using.*send_to_vim', line) == None: + while re.search(self.resultset_end_pattern, line) == None: line = pipe.stdout.readline() buff += line if self.debug: @@ -349,8 +363,11 @@ class SQLWorkbench(object): self.buff = '' #end with with self.executing: - while re.search('send_to_vim', line) == None: + while re.search(self.resultset_end_pattern, line) == None: line = pipe.stdout.readline() + if re.match('^\x1b', line) != None: + continue + #end if if line: if re.search(self.prompt_pattern_begin, line) != None: if not first_prompt: @@ -368,11 +385,15 @@ class SQLWorkbench(object): #end if #end while if self.identifier != None: + self.buff += "Total time: %.2g seconds" % (datetime.datetime.now() - self.clock).total_seconds() + self.clock = datetime.datetime.now() + if self.identifier in self.results: self.results[self.identifier] += "\n" + self.buff else: self.results[self.identifier] = self.buff #end if + self.buff = '' #end if #end with @@ -391,7 +412,7 @@ class SQLWorkbench(object): cmd = "%s -feedback=true -showProgress=false" % (self.cmd) if (self.profile != None): - cmd += " -profile=%s" % self.profile + cmd += " -profile=%s" % profile #end if if self.debug: print "OPENING: " + cmd @@ -412,6 +433,22 @@ class SQLWorkbench(object): with self.lock: self.quit = True #end try...except + try: + pipe.stdin.write('exit\n') + for key in self.dbe_connections: + try: + self.dbe_connections[key].stdin.write('exit\n') + except Exception: + if self.debug: + print "No pipe to send exit for " + key + #end if + #end try + #end for + except Exception: + if self.debug: + print "No pipe to send exit" + #end if + #end try print "Waiting for server to stop..." while self.threads_started > 0: time.sleep(0.3) -- GitLab