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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0a7dab933454e7e153894eb7e0317680dd1f4aa88953480b31d94a825f95f10
4
- data.tar.gz: 6cf9998337f955912eb5a050dfd024bee292afef191a2ee639cda5552e86490c
3
+ metadata.gz: 45285cfe6706fdfa5bae7abf73f1a287e0238709f2f83a18eda8386c71f4de63
4
+ data.tar.gz: 9d3eacf4a4306697c4c34d91b51303dd88b376bec29526124d18ec5c4fbe43d7
5
5
  SHA512:
6
- metadata.gz: f0da91f85d1880eec8ef7d99e5858c49f61334dbabf7d8286b226f34ff9984d5faf30eb4c1be59ea810c4f45e7a148782eadd5d0fb4c488c96403d96d6257b84
7
- data.tar.gz: b1da77e8601b4a7c48bedadcb6e513ef7729893eaad9e30232d1dadb1d600d5ad6f127c28726eb009bbc6abc274369b1fe99d611dc69dae3d353db00c3ea4f59
6
+ metadata.gz: f94e45bf232683013994f63a46eb18257aeaf9d81dd906ccffdbdced023be1743ebca28ff87f34a3b45932757b80516fae979d74a9ab1002affa78a7adb412dc
7
+ data.tar.gz: 4151e2f3f7d795931abfee848f040e9b6066c4ef4ec9503d0f3cdb586e82d3ced59f50c3eb8744c2adc2847324d275d642e313c0ba825768e452e7a0f8af7b75
data/.release-version CHANGED
@@ -1 +1 @@
1
- 0.1.58
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
- database.with_client do |client|
147
- query_result = execute_query(client, variables, queries)
148
- stream do |out|
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
- bytes = json.bytesize
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
- bytes += json.bytesize
164
- break if i == Sqlui::MAX_ROWS || bytes > Sqlui::MAX_BYTES
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
- database.with_client do |client|
202
- query_result = execute_query(client, variables, queries)
203
- stream do |out|
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 { |current| client.query(current) }.last
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
@@ -6,7 +6,7 @@ require_relative 'version'
6
6
 
7
7
  # Main entry point.
8
8
  class Sqlui
9
- MAX_ROWS = 50_000
9
+ MAX_ROWS = 100_000
10
10
  MAX_BYTES = 10 * 1_024 * 1_024 # 10 MB
11
11
 
12
12
  def initialize(config_file)
data/app/views/sqlui.erb CHANGED
@@ -25,24 +25,24 @@
25
25
  </head>
26
26
 
27
27
  <body>
28
- <div id="loading-box" class="loading-box">
28
+ <div id="loading-box">
29
29
  </div>
30
30
 
31
- <div id="main-box" class="main-box" style="display:none">
32
- <div class="tabs-box">
33
- <h1 class="header"><a id="header-link">SQLUI</a></h1>
34
- <h1 id="server-name" class="server-name"></h1>
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="query-box tab-content-element graph-element query-element" style="display: none;">
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="submit-box tab-content-element graph-element query-element" style="display: none;">
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="result-box tab-content-element query-element" style="display: none;">
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="graph-box tab-content-element graph-element" style="display: none;">
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="fetch-sql-box tab-content-element graph-element query-element" style="display: none;">
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="saved-box tab-content-element saved-element" style="display: none;">
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="structure-box tab-content-element structure-element" style="display: none;">
83
+ <div id="structure-box" class="tab-content-element structure-element" style="display: none;">
84
84
  <div class="structure-wrapper">
85
- <select id="schemas" class="schemas" size="4">
85
+ <select id="schemas" size="4">
86
86
  </select>
87
- <select id="tables" class="tables" size="4">
87
+ <select id="tables" size="4">
88
88
  </select>
89
- <div id="table-info" class="table-info">
90
- <div id="columns" class="columns">
89
+ <div id="table-info">
90
+ <div id="columns">
91
91
  </div>
92
- <div id="indexes" class="indexes">
92
+ <div id="indexes">
93
93
  </div>
94
94
  </div>
95
95
  </div>
96
96
  </div>
97
97
 
98
- <div id="status-box" class="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="First" />
104
- <input id="prev-button" class="pagination-button" type="button" value="Prev" />
105
- <input id="next-button" class="pagination-button" type="button" value="Next" />
106
- <input id="last-button" class="pagination-button" type="button" value="Last" />
103
+ <input id="first-button" class="pagination-button" type="button" value="&laquo;" />
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="&lsaquo;" />
108
+ <input id="next-button" class="pagination-button" type="button" value="&rsaquo;" />
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="&raquo;" />
107
113
  </div>
108
114
  </div>
109
115
  </div>
@@ -20,7 +20,7 @@ p {
20
20
  font-size: 18px;
21
21
  }
22
22
 
23
- .loading-box {
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
- .main-box {
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
- .header, .server-name {
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
- .header {
53
+ #header {
54
54
  font-weight: bold;
55
55
  padding-left: 5px;
56
56
  }
57
57
 
58
- .header a {
58
+ #header a {
59
59
  text-decoration: none;
60
60
  color: #333
61
61
  }
62
62
 
63
- .server-name {
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
- .tabs-box {
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
- .query-box {
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
- .submit-box {
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, .submit-button:active, .cancel-button:active, .submit-dropdown-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
- display: flex;
233
- justify-content: center;
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
- .result-box, .fetch-sql-box, .saved-box, .graph-box, .structure-box {
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
- .fetch-sql-box {
280
+ #fetch-sql-box {
272
281
  justify-content: center;
273
282
  align-items: center;
274
283
  }
275
284
 
276
- .graph-box {
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
- .status-box {
356
- padding: 5px 15px;
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
- .tabs-box {
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
- .saved-box h2 {
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
- .saved-box p {
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
- .schemas, .tables {
434
+ #schemas, #tables {
431
435
  border: none;
432
436
  display: flex;
433
437
  min-width: 200px;
434
438
  }
435
439
 
436
- .table-info {
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
- .columns {
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
- .indexes {
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 10px;
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
+ }