app/server.rb in sqlui-0.1.41 vs app/server.rb in sqlui-0.1.42
- old
+ new
@@ -1,11 +1,14 @@
# frozen_string_literal: true
+require 'base64'
+require 'csv'
require 'erb'
require 'json'
require 'sinatra/base'
require 'uri'
+require 'webrick/log'
require_relative 'database_metadata'
require_relative 'mysql_types'
require_relative 'sql_parser'
require_relative 'sqlui'
@@ -22,10 +25,12 @@
set :port, config.port
set :environment, config.environment
set :raise_errors, false
set :show_exceptions, false
+ logger = WEBrick::Log.new
+
get '/-/health' do
status 200
body 'OK'
end
@@ -89,47 +94,58 @@
post "#{database.url_path}/query" do
params.merge!(JSON.parse(request.body.read, symbolize_names: true))
break client_error('missing sql') unless params[:sql]
- full_sql = params[:sql]
- sql = params[:sql]
variables = params[:variables] || {}
- if params[:selection]
- selection = params[:selection]
- if selection.include?('-')
- # sort because the selection could be in either direction
- selection = params[:selection].split('-').map { |v| Integer(v) }.sort
- else
- selection = Integer(selection)
- selection = [selection, selection]
- end
+ sql = find_selected_query(params[:sql], params[:selection])
- sql = if selection[0] == selection[1]
- SqlParser.find_statement_at_cursor(params[:sql], selection[0])
- else
- full_sql[selection[0], selection[1]]
- end
-
- break client_error("can't find query at selection") unless sql
- end
-
result = database.with_client do |client|
- variables.each do |name, value|
- client.query("SET @#{name} = #{value};")
- end
- execute_query(client, sql)
+ query_result = execute_query(client, variables, sql)
+ # NOTE: the call to result.field_types must go before other interaction with the result. Otherwise you will
+ # get a seg fault. Seems to be a bug in Mysql2.
+ # TODO: stream this and render results on the client as they are returned?
+ {
+ columns: query_result.fields,
+ column_types: MysqlTypes.map_to_google_charts_types(query_result.field_types),
+ total_rows: query_result.size,
+ rows: (query_result.to_a || []).take(Sqlui::MAX_ROWS)
+ }
end
result[:selection] = params[:selection]
- result[:query] = full_sql
+ result[:query] = params[:sql]
status 200
headers 'Content-Type' => 'application/json; charset=utf-8'
body result.to_json
end
+ get "#{database.url_path}/download_csv" do
+ break client_error('missing sql') unless params[:sql]
+
+ sql = Base64.decode64(params[:sql]).force_encoding('UTF-8')
+ logger.info "sql: #{sql}"
+ variables = params.map { |k, v| k[0] == '_' ? [k, v] : nil }.compact.to_h
+ sql = find_selected_query(sql, params[:selection])
+ logger.info "sql: #{sql}"
+
+ content_type 'application/csv; charset=utf-8'
+ attachment 'result.csv'
+ status 200
+
+ database.with_client do |client|
+ query_result = execute_query(client, variables, sql)
+ stream do |out|
+ out << CSV::Row.new(query_result.fields, query_result.fields, header_row: true).to_s.strip
+ query_result.each do |row|
+ out << "\n#{CSV::Row.new(query_result.fields, row).to_s.strip}"
+ end
+ end
+ end
+ end
+
get(%r{#{Regexp.escape(database.url_path)}/(query|graph|structure|saved)}) do
@html ||= File.read(File.join(resources_dir, 'sqlui.html'))
status 200
headers 'Content-Type' => 'text/html; charset=utf-8'
body @html
@@ -156,32 +172,35 @@
status(400)
headers 'Content-Type' => 'application/json; charset=utf-8'
body({ error: message, stacktrace: stacktrace }.compact.to_json)
end
- def execute_query(client, sql)
+ def find_selected_query(full_sql, selection)
+ return full_sql unless selection
+
+ if selection.include?('-')
+ # sort because the selection could be in either direction
+ selection = selection.split('-').map { |v| Integer(v) }.sort
+ else
+ selection = Integer(selection)
+ selection = [selection, selection]
+ end
+
+ if selection[0] == selection[1]
+ SqlParser.find_statement_at_cursor(full_sql, selection[0])
+ else
+ full_sql[selection[0], selection[1]]
+ end
+ end
+
+ def execute_query(client, variables, sql)
+ variables.each do |name, value|
+ client.query("SET @#{name} = #{value};")
+ end
queries = if sql.include?(';')
sql.split(/(?<=;)/).map(&:strip).reject(&:empty?)
else
[sql]
end
- results = queries.map { |current| client.query(current) }
- result = results[-1]
- # NOTE: the call to result.field_types must go before any other interaction with the result. Otherwise you will
- # get a seg fault. Seems to be a bug in Mysql2.
- if result
- column_types = MysqlTypes.map_to_google_charts_types(result.field_types)
- rows = result.to_a
- columns = result.fields
- else
- column_types = []
- rows = []
- columns = []
- end
- {
- columns: columns,
- column_types: column_types,
- total_rows: rows.size,
- rows: rows.take(Sqlui::MAX_ROWS)
- }
+ queries.map { |current| client.query(current) }.last
end
end