sqlui 0.1.58 → 0.1.60
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.release-version +1 -1
- data/app/server.rb +57 -13
- data/app/sqlui.rb +1 -1
- data/app/views/sqlui.erb +28 -22
- data/client/resources/sqlui.css +55 -35
- data/client/resources/sqlui.js +357 -103
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45285cfe6706fdfa5bae7abf73f1a287e0238709f2f83a18eda8386c71f4de63
|
4
|
+
data.tar.gz: 9d3eacf4a4306697c4c34d91b51303dd88b376bec29526124d18ec5c4fbe43d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f94e45bf232683013994f63a46eb18257aeaf9d81dd906ccffdbdced023be1743ebca28ff87f34a3b45932757b80516fae979d74a9ab1002affa78a7adb412dc
|
7
|
+
data.tar.gz: 4151e2f3f7d795931abfee848f040e9b6066c4ef4ec9503d0f3cdb586e82d3ced59f50c3eb8744c2adc2847324d275d642e313c0ba825768e452e7a0f8af7b75
|
data/.release-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.60
|
data/app/server.rb
CHANGED
@@ -143,31 +143,60 @@ class Server < Sinatra::Base
|
|
143
143
|
status 200
|
144
144
|
headers 'Content-Type' => 'application/json; charset=utf-8'
|
145
145
|
|
146
|
-
|
147
|
-
|
148
|
-
|
146
|
+
stream do |out|
|
147
|
+
database.with_client do |client|
|
148
|
+
begin
|
149
|
+
query_result = execute_query(client, variables, queries)
|
150
|
+
rescue Mysql2::Error => e
|
151
|
+
stacktrace = e.full_message(highlight: false)
|
152
|
+
message = "ERROR #{e.error_number} (#{e.sql_state}): #{e.message.lines.first&.strip || 'unknown error'}"
|
153
|
+
out << { error: message, stacktrace: stacktrace }.compact.to_json
|
154
|
+
break
|
155
|
+
rescue StandardError => e
|
156
|
+
stacktrace = e.full_message(highlight: false)
|
157
|
+
message = e.message.lines.first&.strip || 'unknown error'
|
158
|
+
out << { error: message, stacktrace: stacktrace }.compact.to_json
|
159
|
+
break
|
160
|
+
end
|
161
|
+
|
149
162
|
if query_result
|
150
163
|
json = <<~RES.chomp
|
151
164
|
{
|
152
165
|
"columns": #{query_result.fields.to_json},
|
153
166
|
"column_types": #{MysqlTypes.map_to_google_charts_types(query_result.field_types).to_json},
|
154
|
-
"total_rows": #{query_result.size.to_json},
|
155
167
|
"selection": #{params[:selection].to_json},
|
156
168
|
"query": #{params[:sql].to_json},
|
157
169
|
"rows": [
|
158
170
|
RES
|
159
171
|
out << json
|
160
|
-
|
172
|
+
bytes_written = json.bytesize
|
173
|
+
max_rows_written = false
|
174
|
+
rows_written = 0
|
175
|
+
total_rows = 0
|
161
176
|
query_result.each_with_index do |row, i|
|
177
|
+
total_rows += 1
|
178
|
+
next if max_rows_written
|
179
|
+
|
162
180
|
json = "#{i.zero? ? '' : ','}\n #{row.map { |v| big_decimal_to_float(v) }.to_json}"
|
163
|
-
|
164
|
-
|
181
|
+
bytesize = json.bytesize
|
182
|
+
if bytes_written + bytesize > Sqlui::MAX_BYTES
|
183
|
+
max_rows_written = true
|
184
|
+
next
|
185
|
+
end
|
165
186
|
|
166
187
|
out << json
|
188
|
+
bytes_written += bytesize
|
189
|
+
rows_written += 1
|
190
|
+
|
191
|
+
if rows_written == Sqlui::MAX_ROWS
|
192
|
+
max_rows_written = true
|
193
|
+
next
|
194
|
+
end
|
167
195
|
end
|
168
196
|
out << <<~RES
|
169
197
|
|
170
|
-
]
|
198
|
+
],
|
199
|
+
"total_rows": #{total_rows}
|
171
200
|
}
|
172
201
|
RES
|
173
202
|
else
|
@@ -198,9 +227,21 @@ class Server < Sinatra::Base
|
|
198
227
|
attachment 'result.csv'
|
199
228
|
status 200
|
200
229
|
|
201
|
-
|
202
|
-
|
203
|
-
|
230
|
+
stream do |out|
|
231
|
+
database.with_client do |client|
|
232
|
+
begin
|
233
|
+
query_result = execute_query(client, variables, queries)
|
234
|
+
rescue Mysql2::Error => e
|
235
|
+
stacktrace = e.full_message(highlight: false)
|
236
|
+
message = "ERROR #{e.error_number} (#{e.sql_state}): #{e.message.lines.first&.strip || 'unknown error'}"
|
237
|
+
out << { error: message, stacktrace: stacktrace }.compact.to_json
|
238
|
+
break
|
239
|
+
rescue StandardError => e
|
240
|
+
stacktrace = e.full_message(highlight: false)
|
241
|
+
message = e.message.lines.first&.strip || 'unknown error'
|
242
|
+
out << { error: message, stacktrace: stacktrace }.compact.to_json
|
243
|
+
break
|
244
|
+
end
|
204
245
|
out << CSV::Row.new(query_result.fields, query_result.fields, header_row: true).to_s.strip
|
205
246
|
query_result.each do |row|
|
206
247
|
out << "\n#{CSV::Row.new(query_result.fields, row.map { |v| big_decimal_to_float(v) }).to_s.strip}"
|
@@ -230,7 +271,7 @@ class Server < Sinatra::Base
|
|
230
271
|
stacktrace = exception&.full_message(highlight: false)
|
231
272
|
if request.env['HTTP_ACCEPT'] == 'application/json'
|
232
273
|
headers 'Content-Type' => 'application/json; charset=utf-8'
|
233
|
-
message = exception&.message&.lines&.first&.strip || 'unexpected error'
|
274
|
+
message = "error: #{exception&.message&.lines&.first&.strip || 'unexpected error'}"
|
234
275
|
json = { error: message, stacktrace: stacktrace }.compact.to_json
|
235
276
|
body json
|
236
277
|
else
|
@@ -274,7 +315,10 @@ class Server < Sinatra::Base
|
|
274
315
|
variables.each do |name, value|
|
275
316
|
client.query("SET @#{name} = #{value};")
|
276
317
|
end
|
277
|
-
queries.map
|
318
|
+
queries[0..-2].map do |current|
|
319
|
+
client.query(current, stream: true)&.free
|
320
|
+
end
|
321
|
+
client.query(queries[-1], stream: true)
|
278
322
|
end
|
279
323
|
|
280
324
|
def big_decimal_to_float(maybe_big_decimal)
|
data/app/sqlui.rb
CHANGED
data/app/views/sqlui.erb
CHANGED
@@ -25,24 +25,24 @@
|
|
25
25
|
</head>
|
26
26
|
|
27
27
|
<body>
|
28
|
-
<div id="loading-box"
|
28
|
+
<div id="loading-box">
|
29
29
|
</div>
|
30
30
|
|
31
|
-
<div id="main-box"
|
32
|
-
<div
|
33
|
-
<h1
|
34
|
-
<h1 id="server-name"
|
31
|
+
<div id="main-box" style="display:none">
|
32
|
+
<div id="tabs-box">
|
33
|
+
<h1 id="header"><a id="header-link">SQLUI</a></h1>
|
34
|
+
<h1 id="server-name"></h1>
|
35
35
|
<a id="query-tab-button" class="tab-button">Query</a>
|
36
36
|
<a id="graph-tab-button" class="tab-button">Graph</a>
|
37
37
|
<a id="saved-tab-button" class="tab-button">Saved</a>
|
38
38
|
<a id="structure-tab-button" class="tab-button">Structure</a>
|
39
39
|
</div>
|
40
40
|
|
41
|
-
<div id="query-box" class="
|
41
|
+
<div id="query-box" class="tab-content-element graph-element query-element" style="display: none;">
|
42
42
|
<div id="query" class="query"></div>
|
43
43
|
</div>
|
44
44
|
|
45
|
-
<div id="submit-box" class="
|
45
|
+
<div id="submit-box" class="tab-content-element graph-element query-element" style="display: none;">
|
46
46
|
<input id="cancel-button" class="cancel-button" type="button" value="cancel"></input>
|
47
47
|
<div class="submit-fill"></div>
|
48
48
|
<div style="position: relative;">
|
@@ -66,44 +66,50 @@
|
|
66
66
|
</div>
|
67
67
|
</div>
|
68
68
|
|
69
|
-
<div id="result-box" class="
|
69
|
+
<div id="result-box" class="tab-content-element query-element" style="display: none;">
|
70
70
|
</div>
|
71
71
|
|
72
|
-
<div id="graph-box" class="
|
72
|
+
<div id="graph-box" class="tab-content-element graph-element" style="display: none;">
|
73
73
|
</div>
|
74
74
|
|
75
|
-
<div id="fetch-sql-box" class="
|
75
|
+
<div id="fetch-sql-box" class="tab-content-element graph-element query-element" style="display: none;">
|
76
76
|
<div id="result-loader" class="loader"></div>
|
77
77
|
<p id="result-time" class="result-time"></p>
|
78
78
|
</div>
|
79
79
|
|
80
|
-
<div id="saved-box" class="
|
80
|
+
<div id="saved-box" class="tab-content-element saved-element" style="display: none;">
|
81
81
|
</div>
|
82
82
|
|
83
|
-
<div id="structure-box" class="
|
83
|
+
<div id="structure-box" class="tab-content-element structure-element" style="display: none;">
|
84
84
|
<div class="structure-wrapper">
|
85
|
-
<select id="schemas"
|
85
|
+
<select id="schemas" size="4">
|
86
86
|
</select>
|
87
|
-
<select id="tables"
|
87
|
+
<select id="tables" size="4">
|
88
88
|
</select>
|
89
|
-
<div id="table-info"
|
90
|
-
<div id="columns"
|
89
|
+
<div id="table-info">
|
90
|
+
<div id="columns">
|
91
91
|
</div>
|
92
|
-
<div id="indexes"
|
92
|
+
<div id="indexes">
|
93
93
|
</div>
|
94
94
|
</div>
|
95
95
|
</div>
|
96
96
|
</div>
|
97
97
|
|
98
|
-
<div id="status-box"
|
98
|
+
<div id="status-box">
|
99
99
|
<div id="status-message"></div>
|
100
100
|
<div style="flex: 1;"></div>
|
101
101
|
<div id="pagination-box" class="tab-content-element">
|
102
102
|
<div id="page-count-box"></div>
|
103
|
-
<input id="first-button" class="pagination-button" type="button" value="
|
104
|
-
<input id="
|
105
|
-
<input id="
|
106
|
-
<input id="
|
103
|
+
<input id="first-button" class="pagination-button" type="button" value="«" />
|
104
|
+
<input id="jb5-button" class="pagination-button jump-button" type="button" value="50" data-jump="-50" data-min="100" />
|
105
|
+
<input id="jb10-button" class="pagination-button jump-button" type="button" value="10" data-jump="-10" data-min="25"/>
|
106
|
+
<input id="jb50-button" class="pagination-button jump-button" type="button" value="5" data-jump="-5" data-min="10" data-max="24"/>
|
107
|
+
<input id="prev-button" class="pagination-button" type="button" value="‹" />
|
108
|
+
<input id="next-button" class="pagination-button" type="button" value="›" />
|
109
|
+
<input id="jf5-button" class="pagination-button jump-button" type="button" value="5" data-jump="5" data-min="10" data-max="24"/>
|
110
|
+
<input id="jf10-button" class="pagination-button jump-button" type="button" value="10" data-jump="10" data-min="25"/>
|
111
|
+
<input id="jf50-button" class="pagination-button jump-button" type="button" value="50" data-jump="50" data-min="100"/>
|
112
|
+
<input id="last-button" class="pagination-button" type="button" value="»" />
|
107
113
|
</div>
|
108
114
|
</div>
|
109
115
|
</div>
|
data/client/resources/sqlui.css
CHANGED
@@ -20,7 +20,7 @@ p {
|
|
20
20
|
font-size: 18px;
|
21
21
|
}
|
22
22
|
|
23
|
-
|
23
|
+
#loading-box {
|
24
24
|
font-family: monospace;
|
25
25
|
display: flex;
|
26
26
|
flex-direction: column;
|
@@ -34,7 +34,7 @@ p {
|
|
34
34
|
color: #333;
|
35
35
|
}
|
36
36
|
|
37
|
-
|
37
|
+
#main-box {
|
38
38
|
display: flex;
|
39
39
|
flex-direction: column;
|
40
40
|
flex: 1;
|
@@ -43,24 +43,24 @@ p {
|
|
43
43
|
min-height: 100%;
|
44
44
|
}
|
45
45
|
|
46
|
-
|
46
|
+
#header, #server-name {
|
47
47
|
display: flex;
|
48
48
|
align-items: center;
|
49
49
|
justify-content: start;
|
50
50
|
color: #333;
|
51
51
|
}
|
52
52
|
|
53
|
-
|
53
|
+
#header {
|
54
54
|
font-weight: bold;
|
55
55
|
padding-left: 5px;
|
56
56
|
}
|
57
57
|
|
58
|
-
|
58
|
+
#header a {
|
59
59
|
text-decoration: none;
|
60
60
|
color: #333
|
61
61
|
}
|
62
62
|
|
63
|
-
|
63
|
+
#server-name {
|
64
64
|
flex: 1;
|
65
65
|
padding-left: 15px;
|
66
66
|
font-weight: normal;
|
@@ -69,12 +69,13 @@ p {
|
|
69
69
|
margin: 0;
|
70
70
|
}
|
71
71
|
|
72
|
-
|
72
|
+
#tabs-box {
|
73
73
|
display: flex;
|
74
74
|
flex-direction: row;
|
75
75
|
border-bottom: 1px solid #ddd;
|
76
76
|
height: 36px;
|
77
77
|
font-family: Helvetica, sans-serif;
|
78
|
+
padding: 5px;
|
78
79
|
}
|
79
80
|
|
80
81
|
.tab-button, .selected-tab-button {
|
@@ -103,7 +104,7 @@ p {
|
|
103
104
|
font-weight: bold;
|
104
105
|
}
|
105
106
|
|
106
|
-
|
107
|
+
#query-box {
|
107
108
|
display: flex;
|
108
109
|
flex-direction: column;
|
109
110
|
}
|
@@ -112,7 +113,7 @@ p {
|
|
112
113
|
font-size: 18px;
|
113
114
|
}
|
114
115
|
|
115
|
-
|
116
|
+
#submit-box {
|
116
117
|
display: flex;
|
117
118
|
border-top: 1px solid #ddd;
|
118
119
|
border-bottom: 1px solid #ddd;
|
@@ -214,7 +215,10 @@ p {
|
|
214
215
|
background-color: #efefef;
|
215
216
|
}
|
216
217
|
|
217
|
-
.submit-dropdown-content-button:active,
|
218
|
+
.submit-dropdown-content-button:active,
|
219
|
+
.submit-button:active,
|
220
|
+
.cancel-button:active,
|
221
|
+
.submit-dropdown-button:active {
|
218
222
|
background-color: #e6e6e6;
|
219
223
|
outline: none
|
220
224
|
}
|
@@ -229,18 +233,18 @@ p {
|
|
229
233
|
}
|
230
234
|
|
231
235
|
#status-message {
|
232
|
-
|
233
|
-
justify-content:
|
234
|
-
align-content: center;
|
235
|
-
flex-direction: row;
|
236
|
+
min-width: 0;
|
237
|
+
justify-content: left;
|
236
238
|
font-family: Helvetica, sans-serif;
|
237
239
|
white-space: nowrap;
|
238
240
|
overflow: hidden;
|
239
241
|
font-size: 16px;
|
240
242
|
color: #333;
|
243
|
+
margin: 0 5px;
|
244
|
+
text-overflow: ellipsis;
|
241
245
|
}
|
242
246
|
|
243
|
-
|
247
|
+
#result-box, #fetch-sql-box, #saved-box, #graph-box, #structure-box {
|
244
248
|
flex: 1;
|
245
249
|
overflow: auto;
|
246
250
|
display: flex;
|
@@ -250,6 +254,10 @@ table tbody tr td {
|
|
250
254
|
height: 21px;
|
251
255
|
}
|
252
256
|
|
257
|
+
#result-table td, #result-table th {
|
258
|
+
cursor: default;
|
259
|
+
}
|
260
|
+
|
253
261
|
#result-table tbody tr td abbr a {
|
254
262
|
color: #555;
|
255
263
|
cursor: pointer;
|
@@ -259,6 +267,7 @@ table tbody tr td {
|
|
259
267
|
border: 1px dotted #555;
|
260
268
|
font-size: 12px;
|
261
269
|
display: inline-block;
|
270
|
+
user-select: none;
|
262
271
|
}
|
263
272
|
|
264
273
|
#result-table tbody tr td abbr {
|
@@ -268,12 +277,12 @@ table tbody tr td {
|
|
268
277
|
bottom: 2px; /* To make the links look vertically centered. */
|
269
278
|
}
|
270
279
|
|
271
|
-
|
280
|
+
#fetch-sql-box {
|
272
281
|
justify-content: center;
|
273
282
|
align-items: center;
|
274
283
|
}
|
275
284
|
|
276
|
-
|
285
|
+
#graph-box {
|
277
286
|
padding: 20px;
|
278
287
|
}
|
279
288
|
|
@@ -352,8 +361,9 @@ thead {
|
|
352
361
|
background: #eee;
|
353
362
|
}
|
354
363
|
|
355
|
-
|
356
|
-
|
364
|
+
#status-box {
|
365
|
+
width: 100%;
|
366
|
+
padding: 5px 0;
|
357
367
|
display: flex;
|
358
368
|
flex-direction: row;
|
359
369
|
border-top: 1px solid #ddd;
|
@@ -366,17 +376,11 @@ thead {
|
|
366
376
|
color: #999;
|
367
377
|
}
|
368
378
|
|
369
|
-
|
370
|
-
display: flex;
|
371
|
-
padding: 5px;
|
372
|
-
}
|
373
|
-
|
374
|
-
.saved-box {
|
379
|
+
#saved-box {
|
375
380
|
font-family: Helvetica, sans-serif;
|
376
381
|
}
|
377
382
|
|
378
|
-
|
379
|
-
margin: 0;
|
383
|
+
#saved-box h2 {
|
380
384
|
font-weight: bold;
|
381
385
|
}
|
382
386
|
|
@@ -384,7 +388,7 @@ thead {
|
|
384
388
|
border-top: none !important;
|
385
389
|
}
|
386
390
|
|
387
|
-
|
391
|
+
#saved-box p {
|
388
392
|
margin: 0;
|
389
393
|
}
|
390
394
|
|
@@ -427,27 +431,27 @@ thead {
|
|
427
431
|
height: 100%;
|
428
432
|
}
|
429
433
|
|
430
|
-
|
434
|
+
#schemas, #tables {
|
431
435
|
border: none;
|
432
436
|
display: flex;
|
433
437
|
min-width: 200px;
|
434
438
|
}
|
435
439
|
|
436
|
-
|
440
|
+
#table-info {
|
437
441
|
display: grid;
|
438
442
|
grid-template-rows: 0.5fr 0.5fr;
|
439
443
|
justify-items: stretch;
|
440
444
|
flex: 1;
|
441
445
|
}
|
442
446
|
|
443
|
-
|
447
|
+
#columns {
|
444
448
|
border-bottom: 1px solid #ddd;
|
445
449
|
overflow: auto;
|
446
450
|
grid-column: 1;
|
447
451
|
grid-row: 1;
|
448
452
|
}
|
449
453
|
|
450
|
-
|
454
|
+
#indexes {
|
451
455
|
overflow: auto;
|
452
456
|
grid-column: 1;
|
453
457
|
grid-row: 2;
|
@@ -486,24 +490,40 @@ select {
|
|
486
490
|
#pagination-box {
|
487
491
|
display: flex;
|
488
492
|
flex-direction: row;
|
493
|
+
margin: 0 5px;
|
489
494
|
}
|
490
495
|
|
491
496
|
#page-count-box {
|
492
497
|
align-self: center;
|
493
498
|
font-size: 16px;
|
499
|
+
white-space: nowrap;
|
494
500
|
}
|
495
501
|
|
496
502
|
.pagination-button {
|
497
|
-
margin: 0 10px;
|
498
|
-
cursor: pointer;
|
503
|
+
margin: 0 0 0 10px;
|
499
504
|
background: none;
|
500
505
|
color: #333;
|
501
506
|
border: 1px solid #888;
|
502
507
|
font-size: 16px;
|
503
|
-
padding: 2px
|
508
|
+
padding: 2px 5px;
|
509
|
+
width: 30px;
|
510
|
+
justify-content: center;
|
511
|
+
}
|
512
|
+
|
513
|
+
.pagination-button:enabled {
|
514
|
+
cursor: pointer;
|
504
515
|
}
|
505
516
|
|
506
517
|
.pagination-button:disabled {
|
507
518
|
color: #888;
|
508
519
|
border: 1px solid #ddd;
|
509
520
|
}
|
521
|
+
|
522
|
+
.pagination-button:active:enabled {
|
523
|
+
background-color: #e6e6e6;
|
524
|
+
outline: none
|
525
|
+
}
|
526
|
+
|
527
|
+
.jump-button {
|
528
|
+
font-size: 12px;
|
529
|
+
}
|