sqlui 0.1.45 → 0.1.47
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.version +1 -1
- data/app/database_config.rb +19 -6
- data/app/server.rb +34 -23
- data/app/sqlui.rb +1 -0
- data/client/resources/sqlui.js +16 -5
- 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: 01c105468ea4c5f463ada429231bd41a3a65752d8fa95f8befa2121a47765fbe
|
4
|
+
data.tar.gz: 6e0df11aa97b0e8d0d685e6b109a297c8ddba78959f109db26f7b159aa4cfa0c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c1fd32e0feb8817bac5e120a15e71adf48e9ccbe5183e0ba729dc29c1a2bb76ffa18bd7c63559dd49f66f0eb8a3669213988701da8c3f4d4566919985c80c13
|
7
|
+
data.tar.gz: 022b592a3ed56c7e06b8222566cfe7d096ed1b1789d687afe4bf945e625a390269dffd8a701fa4647377e85399b40e345147ea637113a2489e31fbd2630e409a
|
data/.version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.47
|
data/app/database_config.rb
CHANGED
@@ -8,7 +8,7 @@ require_relative 'args'
|
|
8
8
|
|
9
9
|
# Config for a single database.
|
10
10
|
class DatabaseConfig
|
11
|
-
attr_reader :display_name, :description, :url_path, :joins, :saved_path, :
|
11
|
+
attr_reader :display_name, :description, :url_path, :joins, :saved_path, :tables, :client_params
|
12
12
|
|
13
13
|
def initialize(hash)
|
14
14
|
@display_name = Args.fetch_non_empty_string(hash, :display_name).strip
|
@@ -27,12 +27,25 @@ class DatabaseConfig
|
|
27
27
|
|
28
28
|
raise ArgumentError, "invalid join #{join.to_json}"
|
29
29
|
end
|
30
|
-
@
|
31
|
-
@
|
32
|
-
|
30
|
+
@tables = Args.fetch_optional_hash(hash, :tables) || {}
|
31
|
+
@tables = @tables.each do |table, table_config|
|
32
|
+
unless table_config.is_a?(Hash)
|
33
|
+
raise ArgumentError, "invalid table config for #{table} (#{table_config}), expected hash"
|
34
|
+
end
|
35
|
+
|
36
|
+
table_alias = table_config[:alias]
|
37
|
+
if table_alias && !table_alias.is_a?(String)
|
38
|
+
raise ArgumentError, "invalid table alias for #{table} (#{table_alias}), expected string"
|
39
|
+
end
|
40
|
+
|
41
|
+
table_boost = table_config[:boost]
|
42
|
+
if table_boost && !table_boost.is_a?(Integer)
|
43
|
+
raise ArgumentError, "invalid table boost for #{table} (#{table_boost}), expected int"
|
44
|
+
end
|
33
45
|
end
|
34
|
-
|
35
|
-
if
|
46
|
+
aliases = @tables.map { |_table, table_config| table_config[:alias] }.compact
|
47
|
+
if aliases.to_set.size < aliases.size
|
48
|
+
duplicate_aliases = aliases.reject { |a| aliases.count(a) == 1 }.to_set
|
36
49
|
raise ArgumentError, "duplicate table aliases: #{duplicate_aliases.join(', ')}"
|
37
50
|
end
|
38
51
|
|
data/app/server.rb
CHANGED
@@ -6,7 +6,6 @@ require 'erb'
|
|
6
6
|
require 'json'
|
7
7
|
require 'sinatra/base'
|
8
8
|
require 'uri'
|
9
|
-
require 'webrick/log'
|
10
9
|
require_relative 'database_metadata'
|
11
10
|
require_relative 'mysql_types'
|
12
11
|
require_relative 'sql_parser'
|
@@ -14,6 +13,10 @@ require_relative 'sqlui'
|
|
14
13
|
|
15
14
|
# SQLUI Sinatra server.
|
16
15
|
class Server < Sinatra::Base
|
16
|
+
def self.logger
|
17
|
+
@logger ||= WEBrick::Log.new
|
18
|
+
end
|
19
|
+
|
17
20
|
def self.init_and_run(config, resources_dir)
|
18
21
|
Mysql2::Client.default_query_options[:as] = :array
|
19
22
|
Mysql2::Client.default_query_options[:cast_booleans] = true
|
@@ -27,8 +30,6 @@ class Server < Sinatra::Base
|
|
27
30
|
set :raise_errors, false
|
28
31
|
set :show_exceptions, false
|
29
32
|
|
30
|
-
logger = WEBrick::Log.new
|
31
|
-
|
32
33
|
get '/-/health' do
|
33
34
|
status 200
|
34
35
|
body 'OK'
|
@@ -67,7 +68,7 @@ class Server < Sinatra::Base
|
|
67
68
|
server: "#{config.name} - #{database.display_name}",
|
68
69
|
list_url_path: config.list_url_path,
|
69
70
|
schemas: DatabaseMetadata.lookup(client, database),
|
70
|
-
|
71
|
+
tables: database.tables,
|
71
72
|
joins: database.joins,
|
72
73
|
saved: Dir.glob("#{database.saved_path}/*.sql").to_h do |path|
|
73
74
|
contents = File.read(path)
|
@@ -99,35 +100,45 @@ class Server < Sinatra::Base
|
|
99
100
|
variables = params[:variables] || {}
|
100
101
|
sql = find_selected_query(params[:sql], params[:selection])
|
101
102
|
|
102
|
-
result = database.with_client do |client|
|
103
|
-
query_result = execute_query(client, variables, sql)
|
104
|
-
# NOTE: the call to result.field_types must go before other interaction with the result. Otherwise you will
|
105
|
-
# get a seg fault. Seems to be a bug in Mysql2.
|
106
|
-
# TODO: stream this and render results on the client as they are returned?
|
107
|
-
{
|
108
|
-
columns: query_result.fields,
|
109
|
-
column_types: MysqlTypes.map_to_google_charts_types(query_result.field_types),
|
110
|
-
total_rows: query_result.size,
|
111
|
-
rows: (query_result.to_a || []).take(Sqlui::MAX_ROWS)
|
112
|
-
}
|
113
|
-
end
|
114
|
-
|
115
|
-
result[:selection] = params[:selection]
|
116
|
-
result[:query] = params[:sql]
|
117
|
-
|
118
103
|
status 200
|
119
104
|
headers 'Content-Type' => 'application/json; charset=utf-8'
|
120
|
-
|
105
|
+
|
106
|
+
database.with_client do |client|
|
107
|
+
query_result = execute_query(client, variables, sql)
|
108
|
+
stream do |out|
|
109
|
+
json = <<~RES.chomp
|
110
|
+
{
|
111
|
+
"columns": #{query_result.fields.to_json},
|
112
|
+
"column_types": #{MysqlTypes.map_to_google_charts_types(query_result.field_types).to_json},
|
113
|
+
"total_rows": #{query_result.size.to_json},
|
114
|
+
"selection": #{params[:selection].to_json},
|
115
|
+
"query": #{params[:sql].to_json},
|
116
|
+
"rows": [
|
117
|
+
RES
|
118
|
+
out << json
|
119
|
+
bytes = json.bytesize
|
120
|
+
query_result.each_with_index do |row, i|
|
121
|
+
json = "#{i.zero? ? '' : ','}\n #{row.to_json}"
|
122
|
+
bytes += json.bytesize
|
123
|
+
break if i == Sqlui::MAX_ROWS || bytes > Sqlui::MAX_BYTES
|
124
|
+
|
125
|
+
out << json
|
126
|
+
end
|
127
|
+
out << <<~RES
|
128
|
+
|
129
|
+
]
|
130
|
+
}
|
131
|
+
RES
|
132
|
+
end
|
133
|
+
end
|
121
134
|
end
|
122
135
|
|
123
136
|
get "#{database.url_path}/download_csv" do
|
124
137
|
break client_error('missing sql') unless params[:sql]
|
125
138
|
|
126
139
|
sql = Base64.decode64(params[:sql]).force_encoding('UTF-8')
|
127
|
-
logger.info "sql: #{sql}"
|
128
140
|
variables = params.map { |k, v| k[0] == '_' ? [k, v] : nil }.compact.to_h
|
129
141
|
sql = find_selected_query(sql, params[:selection])
|
130
|
-
logger.info "sql: #{sql}"
|
131
142
|
|
132
143
|
content_type 'application/csv; charset=utf-8'
|
133
144
|
attachment 'result.csv'
|
data/app/sqlui.rb
CHANGED
data/client/resources/sqlui.js
CHANGED
@@ -23995,19 +23995,20 @@
|
|
23995
23995
|
const schemas = Object.entries(window.metadata.schemas);
|
23996
23996
|
const editorSchema = {};
|
23997
23997
|
const tables = [];
|
23998
|
-
const hasTableAliases = Object.keys(window.metadata.table_aliases).length > 0;
|
23999
23998
|
schemas.forEach(([schemaName, schema]) => {
|
24000
23999
|
Object.entries(schema.tables).forEach(([tableName, table]) => {
|
24001
24000
|
const qualifiedTableName = schemas.length === 1 ? tableName : `${schemaName}.${tableName}`;
|
24002
24001
|
const quotedQualifiedTableName = schemas.length === 1 ? `\`${tableName}\`` : `\`${schemaName}\`.\`${tableName}\``;
|
24003
24002
|
const columns = Object.keys(table.columns);
|
24004
24003
|
editorSchema[qualifiedTableName] = columns;
|
24005
|
-
const alias = window.metadata.
|
24004
|
+
const alias = window.metadata.tables[qualifiedTableName]?.alias;
|
24005
|
+
const boost = window.metadata.tables[qualifiedTableName]?.boost;
|
24006
24006
|
if (alias) {
|
24007
24007
|
editorSchema[alias] = columns;
|
24008
24008
|
tables.push({
|
24009
24009
|
label: qualifiedTableName,
|
24010
24010
|
detail: alias,
|
24011
|
+
boost,
|
24011
24012
|
alias_type: 'with',
|
24012
24013
|
quoted: `${quotedQualifiedTableName} \`${alias}\``,
|
24013
24014
|
unquoted: `${qualifiedTableName} ${alias}`
|
@@ -24015,6 +24016,7 @@
|
|
24015
24016
|
tables.push({
|
24016
24017
|
label: qualifiedTableName,
|
24017
24018
|
detail: alias,
|
24019
|
+
boost,
|
24018
24020
|
alias_type: 'only',
|
24019
24021
|
quoted: '`' + alias + '`',
|
24020
24022
|
unquoted: alias
|
@@ -24090,7 +24092,7 @@
|
|
24090
24092
|
MySQL.language.data.of({
|
24091
24093
|
autocomplete: (context) => {
|
24092
24094
|
const result = originalSchemaCompletionSource(context);
|
24093
|
-
if (!
|
24095
|
+
if (!result?.options) return result
|
24094
24096
|
|
24095
24097
|
const tree = syntaxTree(context.state);
|
24096
24098
|
let node = tree.resolveInner(context.pos, -1);
|
@@ -24164,10 +24166,19 @@
|
|
24164
24166
|
if (foundSchema) {
|
24165
24167
|
const unquotedLabel = unquoteSqlId(option.label);
|
24166
24168
|
const quoted = unquotedLabel !== option.label;
|
24167
|
-
const
|
24169
|
+
const tableConfig = window.metadata.tables[`${foundSchema}.${unquotedLabel}`];
|
24170
|
+
const alias = tableConfig?.alias;
|
24171
|
+
const boost = tableConfig?.boost || -1;
|
24172
|
+
const optionOverride = {
|
24173
|
+
label: option.label
|
24174
|
+
};
|
24168
24175
|
if (alias) {
|
24169
|
-
|
24176
|
+
optionOverride.label = quoted ? `\`${unquotedLabel}\` \`${alias}\`` : `${option.label} ${alias}`;
|
24170
24177
|
}
|
24178
|
+
if (boost) {
|
24179
|
+
optionOverride.boost = boost;
|
24180
|
+
}
|
24181
|
+
if (alias || boost) return optionOverride
|
24171
24182
|
}
|
24172
24183
|
return option
|
24173
24184
|
});
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sqlui
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.47
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Dower
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-11-
|
11
|
+
date: 2022-11-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mysql2
|