sqlui 0.1.45 → 0.1.47
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/.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
|