sqlui 0.1.59 → 0.1.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.release-version +1 -1
- data/app/database_config.rb +2 -2
- data/app/server.rb +97 -45
- data/app/sqlui_config.rb +5 -5
- data/app/views/databases.erb +4 -4
- data/app/views/error.erb +1 -1
- data/app/views/sqlui.erb +8 -6
- data/client/resources/sqlui.css +27 -12
- data/client/resources/sqlui.js +580 -247
- data/client/resources/vertical-resizer.svg +5 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ec12b1d88bc2adff9dba6ff91b01e66b123d17cb8b042e142c24f71303a27a92
|
|
4
|
+
data.tar.gz: df939b59b2bae004270ea1907a6be52352e7c81e6050405bc4534a6faaae0567
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fd504e8ba6cf53b06b30b21867a7925ef7fda8055690eefb7759dd8ed745dd28088ca6c2f257eef1073aa976b67548c155bc26eb4f334750df6a059e9abdf6db
|
|
7
|
+
data.tar.gz: 96584bb97da39f508542a861d16d1bb2196238ab4d12c99528937704f6b29aefee9563c837921dbee1a72fe5113b3a8dfb39b4c74a5951ffc6fb8be697d535cb
|
data/.release-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.61
|
data/app/database_config.rb
CHANGED
|
@@ -14,8 +14,8 @@ class DatabaseConfig
|
|
|
14
14
|
@display_name = Args.fetch_non_empty_string(hash, :display_name).strip
|
|
15
15
|
@description = Args.fetch_non_empty_string(hash, :description).strip
|
|
16
16
|
@url_path = Args.fetch_non_empty_string(hash, :url_path).strip
|
|
17
|
-
raise ArgumentError, 'url_path should start with a /'
|
|
18
|
-
raise ArgumentError, 'url_path should not end with a /' if @url_path.
|
|
17
|
+
raise ArgumentError, 'url_path should not start with a /' if @url_path.start_with?('/')
|
|
18
|
+
raise ArgumentError, 'url_path should not end with a /' if @url_path.end_with?('/')
|
|
19
19
|
|
|
20
20
|
@saved_path = Args.fetch_non_empty_string(hash, :saved_path).strip
|
|
21
21
|
|
data/app/server.rb
CHANGED
|
@@ -25,6 +25,9 @@ class Server < Sinatra::Base
|
|
|
25
25
|
def self.init_and_run(config, resources_dir)
|
|
26
26
|
logger.info("Starting SQLUI v#{Version::SQLUI}")
|
|
27
27
|
logger.info("Airbrake enabled: #{config.airbrake[:server]&.[](:enabled) || false}")
|
|
28
|
+
|
|
29
|
+
WEBrick::HTTPRequest.const_set('MAX_URI_LENGTH', 2 * 1024 * 1024)
|
|
30
|
+
|
|
28
31
|
if config.airbrake[:server]&.[](:enabled)
|
|
29
32
|
require 'airbrake'
|
|
30
33
|
require 'airbrake/rack'
|
|
@@ -59,51 +62,53 @@ class Server < Sinatra::Base
|
|
|
59
62
|
set :raise_errors, false
|
|
60
63
|
set :show_exceptions, false
|
|
61
64
|
|
|
62
|
-
favicon_hash = Digest::MD5.hexdigest(File.read(File.join(resources_dir, 'favicon.svg')))
|
|
63
|
-
css_hash = Digest::MD5.hexdigest(File.read(File.join(resources_dir, 'sqlui.css')))
|
|
64
|
-
js_hash = Digest::MD5.hexdigest(File.read(File.join(resources_dir, 'sqlui.js')))
|
|
65
|
-
|
|
66
65
|
get '/-/health' do
|
|
67
66
|
status 200
|
|
68
67
|
body 'OK'
|
|
69
68
|
end
|
|
70
69
|
|
|
71
70
|
get '/?' do
|
|
72
|
-
redirect config.
|
|
71
|
+
redirect config.base_url_path, 301
|
|
73
72
|
end
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
resource_path_map = {}
|
|
75
|
+
Dir.glob(File.join(resources_dir, '*')).each do |file|
|
|
76
|
+
hash = Digest::MD5.hexdigest(File.read(file))
|
|
77
|
+
basename = File.basename(file)
|
|
78
|
+
url_path = "#{config.base_url_path}/#{basename}"
|
|
79
|
+
case File.extname(basename)
|
|
80
|
+
when '.svg'
|
|
81
|
+
content_type = 'image/svg+xml; charset=utf-8'
|
|
82
|
+
when '.css'
|
|
83
|
+
content_type = 'text/css; charset=utf-8'
|
|
84
|
+
when '.js'
|
|
85
|
+
content_type = 'text/javascript; charset=utf-8'
|
|
86
|
+
else
|
|
87
|
+
raise "unsupported resource file extension: #{File.extname(basename)}"
|
|
88
|
+
end
|
|
89
|
+
resource_path_map[basename] = "#{url_path}?#{hash}"
|
|
90
|
+
get url_path do
|
|
91
|
+
headers 'Content-Type' => content_type
|
|
92
|
+
headers 'Cache-Control' => 'max-age=31536000'
|
|
93
|
+
send_file file
|
|
94
|
+
end
|
|
78
95
|
end
|
|
79
96
|
|
|
80
|
-
get "#{config.
|
|
97
|
+
get "#{config.base_url_path}/?" do
|
|
81
98
|
headers 'Cache-Control' => 'no-cache'
|
|
82
|
-
erb :databases, locals: { config: config }
|
|
99
|
+
erb :databases, locals: { config: config, resource_path_map: resource_path_map }
|
|
83
100
|
end
|
|
84
101
|
|
|
85
102
|
config.database_configs.each do |database|
|
|
86
|
-
get "#{database.url_path}/?" do
|
|
103
|
+
get "#{config.base_url_path}/#{database.url_path}/?" do
|
|
87
104
|
redirect "#{database.url_path}/query", 301
|
|
88
105
|
end
|
|
89
106
|
|
|
90
|
-
|
|
91
|
-
headers 'Content-Type' => 'text/css; charset=utf-8'
|
|
92
|
-
headers 'Cache-Control' => 'max-age=31536000'
|
|
93
|
-
send_file File.join(resources_dir, 'sqlui.css')
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
get "#{database.url_path}/sqlui.js" do
|
|
97
|
-
headers 'Content-Type' => 'text/javascript; charset=utf-8'
|
|
98
|
-
headers 'Cache-Control' => 'max-age=31536000'
|
|
99
|
-
send_file File.join(resources_dir, 'sqlui.js')
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
post "#{database.url_path}/metadata" do
|
|
107
|
+
post "#{config.base_url_path}/#{database.url_path}/metadata" do
|
|
103
108
|
metadata = database.with_client do |client|
|
|
104
109
|
{
|
|
105
110
|
server: "#{config.name} - #{database.display_name}",
|
|
106
|
-
|
|
111
|
+
base_url_path: config.base_url_path,
|
|
107
112
|
schemas: DatabaseMetadata.lookup(client, database),
|
|
108
113
|
tables: database.tables,
|
|
109
114
|
columns: database.columns,
|
|
@@ -131,7 +136,7 @@ class Server < Sinatra::Base
|
|
|
131
136
|
body metadata.to_json
|
|
132
137
|
end
|
|
133
138
|
|
|
134
|
-
post "#{database.url_path}/query" do
|
|
139
|
+
post "#{config.base_url_path}/#{database.url_path}/query" do
|
|
135
140
|
data = request.body.read
|
|
136
141
|
request.body.rewind # since Airbrake will read the body on error
|
|
137
142
|
params.merge!(JSON.parse(data, symbolize_names: true))
|
|
@@ -143,31 +148,60 @@ class Server < Sinatra::Base
|
|
|
143
148
|
status 200
|
|
144
149
|
headers 'Content-Type' => 'application/json; charset=utf-8'
|
|
145
150
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
151
|
+
stream do |out|
|
|
152
|
+
database.with_client do |client|
|
|
153
|
+
begin
|
|
154
|
+
query_result = execute_query(client, variables, queries)
|
|
155
|
+
rescue Mysql2::Error => e
|
|
156
|
+
stacktrace = e.full_message(highlight: false)
|
|
157
|
+
message = "ERROR #{e.error_number} (#{e.sql_state}): #{e.message.lines.first&.strip || 'unknown error'}"
|
|
158
|
+
out << { error: message, stacktrace: stacktrace }.compact.to_json
|
|
159
|
+
break
|
|
160
|
+
rescue StandardError => e
|
|
161
|
+
stacktrace = e.full_message(highlight: false)
|
|
162
|
+
message = e.message.lines.first&.strip || 'unknown error'
|
|
163
|
+
out << { error: message, stacktrace: stacktrace }.compact.to_json
|
|
164
|
+
break
|
|
165
|
+
end
|
|
166
|
+
|
|
149
167
|
if query_result
|
|
150
168
|
json = <<~RES.chomp
|
|
151
169
|
{
|
|
152
170
|
"columns": #{query_result.fields.to_json},
|
|
153
171
|
"column_types": #{MysqlTypes.map_to_google_charts_types(query_result.field_types).to_json},
|
|
154
|
-
"total_rows": #{query_result.size.to_json},
|
|
155
172
|
"selection": #{params[:selection].to_json},
|
|
156
173
|
"query": #{params[:sql].to_json},
|
|
157
174
|
"rows": [
|
|
158
175
|
RES
|
|
159
176
|
out << json
|
|
160
|
-
|
|
177
|
+
bytes_written = json.bytesize
|
|
178
|
+
max_rows_written = false
|
|
179
|
+
rows_written = 0
|
|
180
|
+
total_rows = 0
|
|
161
181
|
query_result.each_with_index do |row, i|
|
|
182
|
+
total_rows += 1
|
|
183
|
+
next if max_rows_written
|
|
184
|
+
|
|
162
185
|
json = "#{i.zero? ? '' : ','}\n #{row.map { |v| big_decimal_to_float(v) }.to_json}"
|
|
163
|
-
|
|
164
|
-
|
|
186
|
+
bytesize = json.bytesize
|
|
187
|
+
if bytes_written + bytesize > Sqlui::MAX_BYTES
|
|
188
|
+
max_rows_written = true
|
|
189
|
+
next
|
|
190
|
+
end
|
|
165
191
|
|
|
166
192
|
out << json
|
|
193
|
+
bytes_written += bytesize
|
|
194
|
+
rows_written += 1
|
|
195
|
+
|
|
196
|
+
if rows_written == Sqlui::MAX_ROWS
|
|
197
|
+
max_rows_written = true
|
|
198
|
+
next
|
|
199
|
+
end
|
|
167
200
|
end
|
|
168
201
|
out << <<~RES
|
|
169
202
|
|
|
170
|
-
]
|
|
203
|
+
],
|
|
204
|
+
"total_rows": #{total_rows}
|
|
171
205
|
}
|
|
172
206
|
RES
|
|
173
207
|
else
|
|
@@ -186,7 +220,7 @@ class Server < Sinatra::Base
|
|
|
186
220
|
end
|
|
187
221
|
end
|
|
188
222
|
|
|
189
|
-
get "#{database.url_path}/download_csv" do
|
|
223
|
+
get "#{config.base_url_path}/#{database.url_path}/download_csv" do
|
|
190
224
|
break client_error('missing sql') unless params[:sql]
|
|
191
225
|
|
|
192
226
|
sql = Base64.decode64(params[:sql]).force_encoding('UTF-8')
|
|
@@ -198,9 +232,21 @@ class Server < Sinatra::Base
|
|
|
198
232
|
attachment 'result.csv'
|
|
199
233
|
status 200
|
|
200
234
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
235
|
+
stream do |out|
|
|
236
|
+
database.with_client do |client|
|
|
237
|
+
begin
|
|
238
|
+
query_result = execute_query(client, variables, queries)
|
|
239
|
+
rescue Mysql2::Error => e
|
|
240
|
+
stacktrace = e.full_message(highlight: false)
|
|
241
|
+
message = "ERROR #{e.error_number} (#{e.sql_state}): #{e.message.lines.first&.strip || 'unknown error'}"
|
|
242
|
+
out << { error: message, stacktrace: stacktrace }.compact.to_json
|
|
243
|
+
break
|
|
244
|
+
rescue StandardError => e
|
|
245
|
+
stacktrace = e.full_message(highlight: false)
|
|
246
|
+
message = e.message.lines.first&.strip || 'unknown error'
|
|
247
|
+
out << { error: message, stacktrace: stacktrace }.compact.to_json
|
|
248
|
+
break
|
|
249
|
+
end
|
|
204
250
|
out << CSV::Row.new(query_result.fields, query_result.fields, header_row: true).to_s.strip
|
|
205
251
|
query_result.each do |row|
|
|
206
252
|
out << "\n#{CSV::Row.new(query_result.fields, row.map { |v| big_decimal_to_float(v) }).to_s.strip}"
|
|
@@ -209,7 +255,7 @@ class Server < Sinatra::Base
|
|
|
209
255
|
end
|
|
210
256
|
end
|
|
211
257
|
|
|
212
|
-
get(
|
|
258
|
+
get(/#{Regexp.escape("#{config.base_url_path}/#{database.url_path}/")}(query|graph|structure|saved)/) do
|
|
213
259
|
status 200
|
|
214
260
|
headers 'Cache-Control' => 'no-cache'
|
|
215
261
|
client_config = config.airbrake[:client] || {}
|
|
@@ -218,9 +264,7 @@ class Server < Sinatra::Base
|
|
|
218
264
|
airbrake_enabled: client_config[:enabled] || false,
|
|
219
265
|
airbrake_project_id: client_config[:project_id] || '',
|
|
220
266
|
airbrake_project_key: client_config[:project_key] || '',
|
|
221
|
-
|
|
222
|
-
css_hash: css_hash,
|
|
223
|
-
favicon_hash: favicon_hash
|
|
267
|
+
resource_path_map: resource_path_map
|
|
224
268
|
}
|
|
225
269
|
end
|
|
226
270
|
end
|
|
@@ -230,12 +274,17 @@ class Server < Sinatra::Base
|
|
|
230
274
|
stacktrace = exception&.full_message(highlight: false)
|
|
231
275
|
if request.env['HTTP_ACCEPT'] == 'application/json'
|
|
232
276
|
headers 'Content-Type' => 'application/json; charset=utf-8'
|
|
233
|
-
message = exception&.message&.lines&.first&.strip || 'unexpected error'
|
|
277
|
+
message = "error: #{exception&.message&.lines&.first&.strip || 'unexpected error'}"
|
|
234
278
|
json = { error: message, stacktrace: stacktrace }.compact.to_json
|
|
235
279
|
body json
|
|
236
280
|
else
|
|
237
281
|
message = "#{status} #{Rack::Utils::HTTP_STATUS_CODES[status]}"
|
|
238
|
-
erb :error, locals: {
|
|
282
|
+
erb :error, locals: {
|
|
283
|
+
resource_path_map: resource_path_map,
|
|
284
|
+
title: "SQLUI #{message}",
|
|
285
|
+
message: message,
|
|
286
|
+
stacktrace: stacktrace
|
|
287
|
+
}
|
|
239
288
|
end
|
|
240
289
|
end
|
|
241
290
|
|
|
@@ -274,7 +323,10 @@ class Server < Sinatra::Base
|
|
|
274
323
|
variables.each do |name, value|
|
|
275
324
|
client.query("SET @#{name} = #{value};")
|
|
276
325
|
end
|
|
277
|
-
queries.map
|
|
326
|
+
queries[0..-2].map do |current|
|
|
327
|
+
client.query(current, stream: true)&.free
|
|
328
|
+
end
|
|
329
|
+
client.query(queries[-1], stream: true)
|
|
278
330
|
end
|
|
279
331
|
|
|
280
332
|
def big_decimal_to_float(maybe_big_decimal)
|
data/app/sqlui_config.rb
CHANGED
|
@@ -8,7 +8,7 @@ require_relative 'deep'
|
|
|
8
8
|
|
|
9
9
|
# App config including database configs.
|
|
10
10
|
class SqluiConfig
|
|
11
|
-
attr_reader :name, :port, :environment, :
|
|
11
|
+
attr_reader :name, :port, :environment, :base_url_path, :database_configs, :airbrake
|
|
12
12
|
|
|
13
13
|
def initialize(filename, overrides = {})
|
|
14
14
|
config = YAML.safe_load(ERB.new(File.read(filename)).result, aliases: true).deep_merge!(overrides)
|
|
@@ -19,10 +19,10 @@ class SqluiConfig
|
|
|
19
19
|
@name = Args.fetch_non_empty_string(config, :name).strip
|
|
20
20
|
@port = Args.fetch_non_empty_int(config, :port)
|
|
21
21
|
@environment = Args.fetch_non_empty_string(config, :environment).strip
|
|
22
|
-
@
|
|
23
|
-
raise ArgumentError, '
|
|
24
|
-
if @
|
|
25
|
-
raise ArgumentError, '
|
|
22
|
+
@base_url_path = Args.fetch_non_empty_string(config, :base_url_path).strip
|
|
23
|
+
raise ArgumentError, 'base_url_path should start with a /' unless @base_url_path.start_with?('/')
|
|
24
|
+
if @base_url_path.length > 1 && @base_url_path.end_with?('/')
|
|
25
|
+
raise ArgumentError, 'base_url_path should not end with a /'
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
databases = Args.fetch_non_empty_hash(config, :databases)
|
data/app/views/databases.erb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<title>SQLUI <%= config.name %> Databases</title>
|
|
6
|
-
<link rel="icon" type="image/x-icon" href="
|
|
6
|
+
<link rel="icon" type="image/x-icon" href="<%= resource_path_map['favicon.svg'] %>">
|
|
7
7
|
|
|
8
8
|
<style>
|
|
9
9
|
body {
|
|
@@ -92,9 +92,9 @@
|
|
|
92
92
|
<div class="database">
|
|
93
93
|
<div class="name-and-links">
|
|
94
94
|
<h2 class='name'><%= database_config.display_name %></h2>
|
|
95
|
-
<a class='query-link' href="<%= database_config.url_path
|
|
96
|
-
<a class='saved-link' href="<%= database_config.url_path
|
|
97
|
-
<a class='structure-link' href="<%= database_config.url_path
|
|
95
|
+
<a class='query-link' href="<%= "#{config.base_url_path}/#{database_config.url_path}/query" %>">query</a>
|
|
96
|
+
<a class='saved-link' href="<%= "#{config.base_url_path}/#{database_config.url_path}/saved" %>">saved</a>
|
|
97
|
+
<a class='structure-link' href="<%= "#{config.base_url_path}/#{database_config.url_path}/structure" %>">structure</a>
|
|
98
98
|
</div>
|
|
99
99
|
<p class='description'>
|
|
100
100
|
<%= database_config.description %>
|
data/app/views/error.erb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<head>
|
|
5
5
|
<meta charset="utf-8">
|
|
6
6
|
<title><%= title %></title>
|
|
7
|
-
<link rel="icon" type="image/x-icon" href="
|
|
7
|
+
<link rel="icon" type="image/x-icon" href="<%= resource_path_map['favicon.svg'] %>">
|
|
8
8
|
</head>
|
|
9
9
|
<body style="font-family: monospace; font-size: 16px;">
|
|
10
10
|
|
data/app/views/sqlui.erb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<title>SQLUI</title>
|
|
6
|
-
<link rel="icon" type="image/x-icon" href="
|
|
6
|
+
<link rel="icon" type="image/x-icon" href="<%= resource_path_map['favicon.svg'] %>">
|
|
7
7
|
<!-- Initialize Airbrake before loading the main app JS so that we can catch errors as early as possible. -->
|
|
8
8
|
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@airbrake/browser"></script>
|
|
9
9
|
<script type="text/javascript">
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
window.airbrake?.notify(error)
|
|
20
20
|
}
|
|
21
21
|
</script>
|
|
22
|
-
<script type="text/javascript" src="sqlui.js
|
|
22
|
+
<script type="text/javascript" src="<%= resource_path_map['sqlui.js'] %>"></script>
|
|
23
23
|
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
|
|
24
|
-
<link rel="stylesheet" href="sqlui.css
|
|
24
|
+
<link rel="stylesheet" href="<%= resource_path_map['sqlui.css'] %>">
|
|
25
25
|
</head>
|
|
26
26
|
|
|
27
27
|
<body>
|
|
@@ -43,10 +43,12 @@
|
|
|
43
43
|
</div>
|
|
44
44
|
|
|
45
45
|
<div id="submit-box" class="tab-content-element graph-element query-element" style="display: none;">
|
|
46
|
-
<input id="cancel-button"
|
|
47
|
-
<div
|
|
46
|
+
<input id="cancel-button" type="button" value="cancel"></input>
|
|
47
|
+
<div id="editor-resizer">
|
|
48
|
+
<img src="<%= resource_path_map['vertical-resizer.svg'] %>" />
|
|
49
|
+
</div>
|
|
48
50
|
<div style="position: relative;">
|
|
49
|
-
<div
|
|
51
|
+
<div id="submit-button-wrapper">
|
|
50
52
|
<input id="submit-button-all" class="submit-button" type="button"></input>
|
|
51
53
|
<input id="submit-button-current" class="submit-button submit-button-show" type="button"></input>
|
|
52
54
|
<input id="submit-dropdown-button" class="submit-dropdown-button" type="button", value="▼"></input>
|
data/client/resources/sqlui.css
CHANGED
|
@@ -120,27 +120,35 @@ p {
|
|
|
120
120
|
padding: 5px;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
#editor-resizer {
|
|
124
124
|
display: flex;
|
|
125
|
-
flex: 1
|
|
125
|
+
flex: 1;
|
|
126
|
+
cursor: row-resize;
|
|
127
|
+
flex-direction: column;
|
|
128
|
+
align-items: center;
|
|
126
129
|
}
|
|
127
130
|
|
|
128
|
-
|
|
131
|
+
#cancel-button {
|
|
129
132
|
cursor: pointer;
|
|
130
|
-
margin: 0;
|
|
131
133
|
background: none;
|
|
132
134
|
color: #333;
|
|
133
135
|
border: 1px solid #888;
|
|
134
136
|
height: 32px;
|
|
135
137
|
font-size: 18px;
|
|
138
|
+
margin: 0 220px 0 0; /* To center the resizer icon. Ok, it's a hack. Get over it. */
|
|
136
139
|
}
|
|
137
140
|
|
|
138
|
-
|
|
141
|
+
#cancel-button-spacer {
|
|
142
|
+
width: 150px;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
#submit-button-wrapper {
|
|
139
146
|
cursor: pointer;
|
|
140
147
|
border: 1px solid #888;
|
|
141
148
|
height: 32px;
|
|
142
149
|
display: flex;
|
|
143
150
|
flex-direction: row;
|
|
151
|
+
background: white;
|
|
144
152
|
}
|
|
145
153
|
|
|
146
154
|
.submit-button, .submit-dropdown-button {
|
|
@@ -217,7 +225,7 @@ p {
|
|
|
217
225
|
|
|
218
226
|
.submit-dropdown-content-button:active,
|
|
219
227
|
.submit-button:active,
|
|
220
|
-
|
|
228
|
+
#cancel-button:active,
|
|
221
229
|
.submit-dropdown-button:active {
|
|
222
230
|
background-color: #e6e6e6;
|
|
223
231
|
outline: none
|
|
@@ -233,16 +241,15 @@ p {
|
|
|
233
241
|
}
|
|
234
242
|
|
|
235
243
|
#status-message {
|
|
236
|
-
|
|
237
|
-
justify-content:
|
|
238
|
-
align-content: center;
|
|
239
|
-
flex-direction: row;
|
|
244
|
+
min-width: 0;
|
|
245
|
+
justify-content: left;
|
|
240
246
|
font-family: Helvetica, sans-serif;
|
|
241
247
|
white-space: nowrap;
|
|
242
248
|
overflow: hidden;
|
|
243
249
|
font-size: 16px;
|
|
244
250
|
color: #333;
|
|
245
|
-
margin
|
|
251
|
+
margin: 0 5px;
|
|
252
|
+
text-overflow: ellipsis;
|
|
246
253
|
}
|
|
247
254
|
|
|
248
255
|
#result-box, #fetch-sql-box, #saved-box, #graph-box, #structure-box {
|
|
@@ -255,6 +262,10 @@ table tbody tr td {
|
|
|
255
262
|
height: 21px;
|
|
256
263
|
}
|
|
257
264
|
|
|
265
|
+
#result-table td, #result-table th {
|
|
266
|
+
cursor: default;
|
|
267
|
+
}
|
|
268
|
+
|
|
258
269
|
#result-table tbody tr td abbr a {
|
|
259
270
|
color: #555;
|
|
260
271
|
cursor: pointer;
|
|
@@ -264,6 +275,7 @@ table tbody tr td {
|
|
|
264
275
|
border: 1px dotted #555;
|
|
265
276
|
font-size: 12px;
|
|
266
277
|
display: inline-block;
|
|
278
|
+
user-select: none;
|
|
267
279
|
}
|
|
268
280
|
|
|
269
281
|
#result-table tbody tr td abbr {
|
|
@@ -358,7 +370,8 @@ thead {
|
|
|
358
370
|
}
|
|
359
371
|
|
|
360
372
|
#status-box {
|
|
361
|
-
|
|
373
|
+
width: 100%;
|
|
374
|
+
padding: 5px 0;
|
|
362
375
|
display: flex;
|
|
363
376
|
flex-direction: row;
|
|
364
377
|
border-top: 1px solid #ddd;
|
|
@@ -485,11 +498,13 @@ select {
|
|
|
485
498
|
#pagination-box {
|
|
486
499
|
display: flex;
|
|
487
500
|
flex-direction: row;
|
|
501
|
+
margin: 0 5px;
|
|
488
502
|
}
|
|
489
503
|
|
|
490
504
|
#page-count-box {
|
|
491
505
|
align-self: center;
|
|
492
506
|
font-size: 16px;
|
|
507
|
+
white-space: nowrap;
|
|
493
508
|
}
|
|
494
509
|
|
|
495
510
|
.pagination-button {
|