app/server.rb in sqlui-0.1.59 vs app/server.rb in sqlui-0.1.60

- old
+ new

@@ -141,35 +141,64 @@ queries = find_selected_queries(params[:sql], params[:selection]) status 200 headers 'Content-Type' => 'application/json; charset=utf-8' - database.with_client do |client| - query_result = execute_query(client, variables, queries) - stream do |out| + stream do |out| + database.with_client do |client| + begin + query_result = execute_query(client, variables, queries) + rescue Mysql2::Error => e + stacktrace = e.full_message(highlight: false) + message = "ERROR #{e.error_number} (#{e.sql_state}): #{e.message.lines.first&.strip || 'unknown error'}" + out << { error: message, stacktrace: stacktrace }.compact.to_json + break + rescue StandardError => e + stacktrace = e.full_message(highlight: false) + message = e.message.lines.first&.strip || 'unknown error' + out << { error: message, stacktrace: stacktrace }.compact.to_json + break + end + if query_result json = <<~RES.chomp { "columns": #{query_result.fields.to_json}, "column_types": #{MysqlTypes.map_to_google_charts_types(query_result.field_types).to_json}, - "total_rows": #{query_result.size.to_json}, "selection": #{params[:selection].to_json}, "query": #{params[:sql].to_json}, "rows": [ RES out << json - bytes = json.bytesize + bytes_written = json.bytesize + max_rows_written = false + rows_written = 0 + total_rows = 0 query_result.each_with_index do |row, i| + total_rows += 1 + next if max_rows_written + json = "#{i.zero? ? '' : ','}\n #{row.map { |v| big_decimal_to_float(v) }.to_json}" - bytes += json.bytesize - break if i == Sqlui::MAX_ROWS || bytes > Sqlui::MAX_BYTES + bytesize = json.bytesize + if bytes_written + bytesize > Sqlui::MAX_BYTES + max_rows_written = true + next + end out << json + bytes_written += bytesize + rows_written += 1 + + if rows_written == Sqlui::MAX_ROWS + max_rows_written = true + next + end end out << <<~RES - ] + ], + "total_rows": #{total_rows} } RES else out << <<~RES { @@ -196,13 +225,25 @@ content_type 'application/csv; charset=utf-8' headers 'Cache-Control' => 'no-cache' attachment 'result.csv' status 200 - database.with_client do |client| - query_result = execute_query(client, variables, queries) - stream do |out| + stream do |out| + database.with_client do |client| + begin + query_result = execute_query(client, variables, queries) + rescue Mysql2::Error => e + stacktrace = e.full_message(highlight: false) + message = "ERROR #{e.error_number} (#{e.sql_state}): #{e.message.lines.first&.strip || 'unknown error'}" + out << { error: message, stacktrace: stacktrace }.compact.to_json + break + rescue StandardError => e + stacktrace = e.full_message(highlight: false) + message = e.message.lines.first&.strip || 'unknown error' + out << { error: message, stacktrace: stacktrace }.compact.to_json + break + end 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.map { |v| big_decimal_to_float(v) }).to_s.strip}" end end @@ -228,11 +269,11 @@ error 400..510 do exception = env['sinatra.error'] stacktrace = exception&.full_message(highlight: false) if request.env['HTTP_ACCEPT'] == 'application/json' headers 'Content-Type' => 'application/json; charset=utf-8' - message = exception&.message&.lines&.first&.strip || 'unexpected error' + message = "error: #{exception&.message&.lines&.first&.strip || 'unexpected error'}" json = { error: message, stacktrace: stacktrace }.compact.to_json body json else message = "#{status} #{Rack::Utils::HTTP_STATUS_CODES[status]}" erb :error, locals: { title: "SQLUI #{message}", message: message, stacktrace: stacktrace } @@ -272,10 +313,13 @@ def execute_query(client, variables, queries) variables.each do |name, value| client.query("SET @#{name} = #{value};") end - queries.map { |current| client.query(current) }.last + queries[0..-2].map do |current| + client.query(current, stream: true)&.free + end + client.query(queries[-1], stream: true) end def big_decimal_to_float(maybe_big_decimal) # TODO: This BigDecimal thing needs some thought. if maybe_big_decimal.is_a?(BigDecimal)