PageRenderTime 23ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/Bundles/tmBundles/SQL.tmbundle/Support/bin/db_browser.rb

http://luapack.googlecode.com/
Ruby | 211 lines | 176 code | 21 blank | 14 comment | 22 complexity | 96389b6cf4aa7cebe012fd5bfb4cac4a MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0, AGPL-3.0, LGPL-2.0, LGPL-3.0, BSD-3-Clause, ISC
  1. #!/usr/bin/env ruby
  2. # encoding: utf-8
  3. require 'optparse'
  4. require 'ostruct'
  5. require 'erb'
  6. require 'iconv'
  7. require File.dirname(__FILE__) + '/db_browser_lib'
  8. require ENV['TM_BUNDLE_SUPPORT'] + '/lib/json'
  9. require 'cgi'
  10. require "#{ENV["TM_SUPPORT_PATH"]}/lib/web_preview" if ENV["TM_SUPPORT_PATH"]
  11. NO_TABLE = '__none__'
  12. @options = OpenStruct.new
  13. @options.offset = 0
  14. @options.page_size = 10
  15. @options.mode = 'home'
  16. @options.database = OpenStruct.new
  17. @options.database.server = 'mysql'
  18. @options.database.host = nil
  19. @options.database.user = nil
  20. @options.database.password = nil
  21. @options.database.port = nil
  22. @options.database.name = nil
  23. @options.database.table = nil
  24. require "#{ENV["TM_SUPPORT_PATH"]}/lib/osx/plist" if ENV["TM_SUPPORT_PATH"]
  25. # Parse commandline options
  26. begin
  27. OptionParser.new do |opts|
  28. opts.banner = "Usage: db_browser.rb [options]"
  29. opts.on("--database database", "Set database name") { |database| @options.database.name = database }
  30. opts.on("--table table", "Set database table") { |table| @options.database.table = table }
  31. opts.on("--query query", "Run query on database") { |query| @options.query = query.to_s.strip }
  32. opts.on("--rows rows", OptionParser::DecimalInteger, "Set page size for query output") { |rows| @options.page_size = rows }
  33. opts.on("--offset offset", OptionParser::DecimalInteger, "Set offset") { |offset| @options.offset = offset }
  34. opts.on('--mode=mode', ['home', 'frame', 'tables', 'search']) { |mode| @options.mode = mode }
  35. opts.on('--version', 'Print mysql server version and exit') { @options.mode = 'version' }
  36. opts.on_tail("-h", "--help", "Show this message") { puts opts; exit }
  37. end.parse!
  38. rescue OptionParser::InvalidOption, OptionParser::InvalidArgument, OptionParser::AmbiguousArgument => e
  39. puts e.reason + ": " + e.args.pop
  40. exit
  41. end
  42. begin
  43. get_connection_settings(@options.database)
  44. @connection = get_connection
  45. rescue MissingConfigurationException
  46. # Show initial setup message
  47. html('Configuration') do
  48. print File.read(ENV['TM_BUNDLE_SUPPORT'] + '/install.html')
  49. end
  50. rescue ConnectorException => error
  51. html('Connection Error') do
  52. puts <<-HTML
  53. <h1>Connection Error</h1>
  54. <blockquote>
  55. #{error.message}
  56. </blockquote>
  57. <p>Please correct your connection settings in the <a href="javascript:launchConfig()">configuration dialog</a>.</p>
  58. HTML
  59. end
  60. end
  61. if @options.mode == 'version'
  62. puts @connection.server_version
  63. exit
  64. end
  65. def print_data(query = nil)
  66. @page_size = @options.page_size
  67. offset = @options.offset
  68. if not query or query.to_s.size == 0
  69. query = "SELECT * FROM %s" % @options.database.table
  70. @page_size = 5
  71. offset = 0
  72. end
  73. run_query = query.sub(/;\s*$/, '')
  74. @limited = true
  75. if not query=~ /\bLIMIT\b/i and run_query =~ /\s*SELECT/i
  76. run_query << ' LIMIT %d OFFSET %d' % [@page_size, offset]
  77. @limited = false
  78. end
  79. @query = query
  80. begin
  81. @result = @connection.do_query(run_query)
  82. if @result.is_a? Result
  83. @title = 'Query complete'
  84. if @result.num_rows == 0
  85. @message = 'There are no records to show'
  86. else
  87. @pager = 'Records %d to %d' % [offset + 1, offset + @result.num_rows]
  88. end
  89. else
  90. @message = @result.to_s + " row#{:s if @result.to_i != 1} affected"
  91. end
  92. rescue Exception => e
  93. @title = "Invalid query"
  94. if defined?(Mysql::Error) and e.is_a? Mysql::Error
  95. @message = escape(smarty(e.message))
  96. elsif e.is_a? RuntimeError # used by Postgresql connector
  97. # This is my best guess at decyphering the error messages returned by Postgresql
  98. # I've added the rescue as a fallback
  99. @message = e.message.split("\t")[2][1..-1] rescue e.message
  100. else
  101. # This is a code error (this will never be reached, of course ;)
  102. @message = "<b>#{e.class.name}: #{escape(smarty(e.message))}</b>"
  103. @message += '<pre>' + "\t" + escape(e.backtrace.join("\n\t")) + '</pre>'
  104. end
  105. end
  106. render('result')
  107. end
  108. # ====================
  109. # = Template helpers =
  110. # ====================
  111. def smarty(text)
  112. text.to_s.sub(" -- ", ' â&#x20AC;&#x201D; ')
  113. end
  114. def escape(text)
  115. CGI.escapeHTML(text.to_s)
  116. end
  117. def e_js(str)
  118. str.to_s.gsub(/(?=['\\])/, '\\')
  119. end
  120. def format(content, type)
  121. return '' unless content
  122. begin
  123. Iconv.iconv('utf-8', 'utf-8', content)
  124. rescue Exception => e
  125. content = content.unpack('C*').map { |ch| ch < 128 ? ch : ?? }.pack('C*')
  126. end
  127. if type == :number
  128. content
  129. else
  130. full_content = escape(content)
  131. content = escape(content).gsub("\n", "<br>")
  132. if content.length > 30
  133. content = content[/\A.{29}\w*/m] + '<span style="color: red;font-weight: bold">' + 'â&#x20AC;?</span>'
  134. end
  135. tag = '<span'
  136. tag << ' title="' + full_content + '"' if content != full_content
  137. tag << '>' + content + '</span>'
  138. end
  139. end
  140. def get_data_link(link, new_params = {})
  141. params = []
  142. params << "'" + e_js(escape((new_params[:database] || @options.database.name).to_s)) + "'"
  143. params << "'" + e_js(escape((new_params[:table] || @options.database.table).to_s)) + "'"
  144. params << (new_params[:query] ? escape(new_params[:query].inspect) : "''")
  145. params << @options.page_size.to_i
  146. params << new_params[:offset].to_i
  147. '<a href="javascript:getData(' + params.join(', ') + ')">' + link + "</a>"
  148. end
  149. # ===============
  150. # = Entry point =
  151. # ===============
  152. if @options.mode == 'tables'
  153. @tables = @connection.table_list(@options.database.name)
  154. print render('tables')
  155. elsif @options.mode == 'home'
  156. html do
  157. STDOUT.flush
  158. @content = ''
  159. if @options.query.to_s.size > 0
  160. @content = print_data(@options.query)
  161. elsif ENV['TM_BUNDLE_SUPPORT']
  162. @content = '<h2>Database Browser</h2>Please choose a table from the left'
  163. end
  164. begin
  165. @databases = @connection.database_list
  166. rescue ConnectorException => e
  167. abort e.message
  168. end
  169. print render('main')
  170. end
  171. elsif @options.mode == 'search'
  172. query = 'SELECT * FROM ' + @options.database.table
  173. conditions = JSON.parse(STDIN.read)
  174. unless conditions.empty?
  175. query << ' WHERE '
  176. conditions.each_pair do |field, value|
  177. query << @connection.quote_field(field) + " = " + @connection.quote_value(value)
  178. end
  179. end
  180. print print_data(query)
  181. elsif @options.query.to_s.size > 0
  182. print print_data(@options.query)
  183. elsif @options.database.table
  184. if @options.database.table != NO_TABLE
  185. @table = @options.database.table
  186. @fields = @connection.get_fields
  187. print render('columns')
  188. print print_data
  189. end
  190. end