sqlui 0.1.40 → 0.1.41
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/args.rb +4 -0
- data/app/database_config.rb +11 -1
- data/app/database_metadata.rb +20 -22
- data/app/server.rb +8 -2
- data/app/sqlui_config.rb +1 -1
- data/client/resources/sqlui.js +86 -31
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4307dfb13604d60040248f4a4135756f52310be559dfd3a65778e8400ab5454
|
4
|
+
data.tar.gz: 00ea3b80ba354e767521943cf83058cceebd53d5d3f112fdabc09952ee375471
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1aab467177964e30d47233e5f8e60922732de9cdb0238104dc7b74eecf9271ce80e3ac449c3afaac5c4ea91863160cd7edbf352b88cc8ae11274b738abe60271
|
7
|
+
data.tar.gz: 2d644c6ecd81b489acad2986be0d60f0dee3c20e5165943938279148af8f4f9f7a923a410bed570be71aad99b3f591ff92c050cd2a27df9ae06bdbb4b63ef8d7
|
data/.version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.41
|
data/app/args.rb
CHANGED
@@ -24,6 +24,10 @@ class Args
|
|
24
24
|
fetch_optional(hash, key, Hash)
|
25
25
|
end
|
26
26
|
|
27
|
+
def self.fetch_optional_array(hash, key)
|
28
|
+
fetch_optional(hash, key, Array)
|
29
|
+
end
|
30
|
+
|
27
31
|
def self.fetch_non_nil(hash, key, *classes)
|
28
32
|
raise ArgumentError, "required parameter #{key} missing" unless hash.key?(key)
|
29
33
|
|
data/app/database_config.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
3
4
|
require 'mysql2'
|
4
5
|
require 'set'
|
5
6
|
|
@@ -7,7 +8,7 @@ require_relative 'args'
|
|
7
8
|
|
8
9
|
# Config for a single database.
|
9
10
|
class DatabaseConfig
|
10
|
-
attr_reader :display_name, :description, :url_path, :saved_path, :table_aliases, :client_params
|
11
|
+
attr_reader :display_name, :description, :url_path, :joins, :saved_path, :table_aliases, :client_params
|
11
12
|
|
12
13
|
def initialize(hash)
|
13
14
|
@display_name = Args.fetch_non_empty_string(hash, :display_name).strip
|
@@ -17,6 +18,15 @@ class DatabaseConfig
|
|
17
18
|
raise ArgumentError, 'url_path should not end with a /' if @url_path.length > 1 && @url_path.end_with?('/')
|
18
19
|
|
19
20
|
@saved_path = Args.fetch_non_empty_string(hash, :saved_path).strip
|
21
|
+
@joins = Args.fetch_optional_array(hash, :joins) || []
|
22
|
+
@joins.map do |join|
|
23
|
+
next if join.is_a?(Hash) &&
|
24
|
+
join.keys.size == 2 &&
|
25
|
+
join[:label].is_a?(String) && !join[:label].strip.empty? &&
|
26
|
+
join[:apply].is_a?(String) && !join[:apply].strip.empty?
|
27
|
+
|
28
|
+
raise ArgumentError, "invalid join #{join.to_json}"
|
29
|
+
end
|
20
30
|
@table_aliases = Args.fetch_optional_hash(hash, :table_aliases) || {}
|
21
31
|
@table_aliases = @table_aliases.each do |table, a|
|
22
32
|
raise ArgumentError, "invalid alias for table #{table} (#{a}), expected string" unless a.is_a?(String)
|
data/app/database_metadata.rb
CHANGED
@@ -38,18 +38,17 @@ class DatabaseMetadata
|
|
38
38
|
extra
|
39
39
|
from information_schema.columns
|
40
40
|
#{where_clause}
|
41
|
-
order by table_schema, table_name,
|
41
|
+
order by table_schema, table_name, ordinal_position;
|
42
42
|
SQL
|
43
43
|
)
|
44
|
-
column_result.each do |row|
|
45
|
-
|
46
|
-
table_schema = row[:table_schema]
|
44
|
+
column_result.to_a.each do |row|
|
45
|
+
table_schema = row.shift
|
47
46
|
unless result[table_schema]
|
48
47
|
result[table_schema] = {
|
49
48
|
tables: {}
|
50
49
|
}
|
51
50
|
end
|
52
|
-
table_name = row
|
51
|
+
table_name = row.shift
|
53
52
|
tables = result[table_schema][:tables]
|
54
53
|
unless tables[table_name]
|
55
54
|
tables[table_name] = {
|
@@ -58,16 +57,16 @@ class DatabaseMetadata
|
|
58
57
|
}
|
59
58
|
end
|
60
59
|
columns = result[table_schema][:tables][table_name][:columns]
|
61
|
-
column_name = row
|
60
|
+
column_name = row.shift
|
62
61
|
columns[column_name] = {} unless columns[column_name]
|
63
62
|
column = columns[column_name]
|
64
63
|
column[:name] = column_name
|
65
|
-
column[:data_type] = row
|
66
|
-
column[:length] = row
|
67
|
-
column[:allow_null] = row
|
68
|
-
column[:key] = row
|
69
|
-
column[:default] = row
|
70
|
-
column[:extra] = row
|
64
|
+
column[:data_type] = row.shift
|
65
|
+
column[:length] = row.shift
|
66
|
+
column[:allow_null] = row.shift
|
67
|
+
column[:key] = row.shift
|
68
|
+
column[:default] = row.shift
|
69
|
+
column[:extra] = row.shift
|
71
70
|
end
|
72
71
|
result
|
73
72
|
end
|
@@ -86,30 +85,29 @@ class DatabaseMetadata
|
|
86
85
|
table_schema,
|
87
86
|
table_name,
|
88
87
|
index_name,
|
88
|
+
column_name,
|
89
89
|
seq_in_index,
|
90
|
-
non_unique
|
91
|
-
column_name
|
90
|
+
non_unique
|
92
91
|
from information_schema.statistics
|
93
92
|
#{where_clause}
|
94
93
|
order by table_schema, table_name, if(index_name = "PRIMARY", 0, index_name), seq_in_index;
|
95
94
|
SQL
|
96
95
|
)
|
97
96
|
stats_result.each do |row|
|
98
|
-
|
99
|
-
table_schema = row[:table_schema]
|
97
|
+
table_schema = row.shift
|
100
98
|
tables = result[table_schema][:tables]
|
101
|
-
table_name = row
|
99
|
+
table_name = row.shift
|
102
100
|
indexes = tables[table_name][:indexes]
|
103
|
-
index_name = row
|
101
|
+
index_name = row.shift
|
104
102
|
indexes[index_name] = {} unless indexes[index_name]
|
105
103
|
index = indexes[index_name]
|
106
|
-
column_name = row
|
104
|
+
column_name = row.shift
|
107
105
|
index[column_name] = {}
|
108
106
|
column = index[column_name]
|
109
107
|
column[:name] = index_name
|
110
|
-
column[:seq_in_index] = row
|
111
|
-
column[:non_unique] = row
|
112
|
-
column[:column_name] =
|
108
|
+
column[:seq_in_index] = row.shift
|
109
|
+
column[:non_unique] = row.shift
|
110
|
+
column[:column_name] = column_name
|
113
111
|
end
|
114
112
|
end
|
115
113
|
end
|
data/app/server.rb
CHANGED
@@ -12,6 +12,11 @@ require_relative 'sqlui'
|
|
12
12
|
# SQLUI Sinatra server.
|
13
13
|
class Server < Sinatra::Base
|
14
14
|
def self.init_and_run(config, resources_dir)
|
15
|
+
Mysql2::Client.default_query_options[:as] = :array
|
16
|
+
Mysql2::Client.default_query_options[:cast_booleans] = true
|
17
|
+
Mysql2::Client.default_query_options[:database_timezone] = :utc
|
18
|
+
Mysql2::Client.default_query_options[:cache_rows] = false
|
19
|
+
|
15
20
|
set :logging, true
|
16
21
|
set :bind, '0.0.0.0'
|
17
22
|
set :port, config.port
|
@@ -58,6 +63,7 @@ class Server < Sinatra::Base
|
|
58
63
|
list_url_path: config.list_url_path,
|
59
64
|
schemas: DatabaseMetadata.lookup(client, database),
|
60
65
|
table_aliases: database.table_aliases,
|
66
|
+
joins: database.joins,
|
61
67
|
saved: Dir.glob("#{database.saved_path}/*.sql").to_h do |path|
|
62
68
|
contents = File.read(path)
|
63
69
|
comment_lines = contents.split("\n").take_while do |l|
|
@@ -164,8 +170,8 @@ class Server < Sinatra::Base
|
|
164
170
|
# get a seg fault. Seems to be a bug in Mysql2.
|
165
171
|
if result
|
166
172
|
column_types = MysqlTypes.map_to_google_charts_types(result.field_types)
|
167
|
-
rows = result.
|
168
|
-
columns = result.
|
173
|
+
rows = result.to_a
|
174
|
+
columns = result.fields
|
169
175
|
else
|
170
176
|
column_types = []
|
171
177
|
rows = []
|
data/app/sqlui_config.rb
CHANGED
@@ -11,7 +11,7 @@ class SqluiConfig
|
|
11
11
|
attr_reader :name, :port, :environment, :list_url_path, :database_configs
|
12
12
|
|
13
13
|
def initialize(filename, overrides = {})
|
14
|
-
config = YAML.safe_load(ERB.new(File.read(filename)).result).deep_merge!(overrides)
|
14
|
+
config = YAML.safe_load(ERB.new(File.read(filename)).result, aliases: true).deep_merge!(overrides)
|
15
15
|
config.deep_symbolize_keys!
|
16
16
|
@name = Args.fetch_non_empty_string(config, :name).strip
|
17
17
|
@port = Args.fetch_non_empty_int(config, :port)
|
data/client/resources/sqlui.js
CHANGED
@@ -3359,23 +3359,24 @@
|
|
3359
3359
|
*/
|
3360
3360
|
minPointSize = -1) {
|
3361
3361
|
let cursor = new SpanCursor(sets, null, minPointSize).goto(from), pos = from;
|
3362
|
-
let
|
3362
|
+
let openRanges = cursor.openStart;
|
3363
3363
|
for (;;) {
|
3364
3364
|
let curTo = Math.min(cursor.to, to);
|
3365
3365
|
if (cursor.point) {
|
3366
|
-
|
3367
|
-
|
3366
|
+
let active = cursor.activeForPoint(cursor.to);
|
3367
|
+
let openCount = cursor.pointFrom < from ? active.length + 1 : Math.min(active.length, openRanges);
|
3368
|
+
iterator.point(pos, curTo, cursor.point, active, openCount, cursor.pointRank);
|
3369
|
+
openRanges = Math.min(cursor.openEnd(curTo), active.length);
|
3368
3370
|
}
|
3369
3371
|
else if (curTo > pos) {
|
3370
|
-
iterator.span(pos, curTo, cursor.active,
|
3371
|
-
|
3372
|
+
iterator.span(pos, curTo, cursor.active, openRanges);
|
3373
|
+
openRanges = cursor.openEnd(curTo);
|
3372
3374
|
}
|
3373
3375
|
if (cursor.to > to)
|
3374
|
-
|
3376
|
+
return openRanges + (cursor.point && cursor.to > to ? 1 : 0);
|
3375
3377
|
pos = cursor.to;
|
3376
3378
|
cursor.next();
|
3377
3379
|
}
|
3378
|
-
return open;
|
3379
3380
|
}
|
3380
3381
|
/**
|
3381
3382
|
Create a range set for the given range or array of ranges. By
|
@@ -3679,6 +3680,8 @@
|
|
3679
3680
|
this.pointRank = 0;
|
3680
3681
|
this.to = -1000000000 /* C.Far */;
|
3681
3682
|
this.endSide = 0;
|
3683
|
+
// The amount of open active ranges at the start of the iterator.
|
3684
|
+
// Not including points.
|
3682
3685
|
this.openStart = -1;
|
3683
3686
|
this.cursor = HeapCursor.from(sets, skip, minPoint);
|
3684
3687
|
}
|
@@ -3719,7 +3722,7 @@
|
|
3719
3722
|
next() {
|
3720
3723
|
let from = this.to, wasPoint = this.point;
|
3721
3724
|
this.point = null;
|
3722
|
-
let trackOpen = this.openStart < 0 ? [] : null
|
3725
|
+
let trackOpen = this.openStart < 0 ? [] : null;
|
3723
3726
|
for (;;) {
|
3724
3727
|
let a = this.minActive;
|
3725
3728
|
if (a > -1 && (this.activeTo[a] - this.cursor.from || this.active[a].endSide - this.cursor.startSide) < 0) {
|
@@ -3745,8 +3748,6 @@
|
|
3745
3748
|
let nextVal = this.cursor.value;
|
3746
3749
|
if (!nextVal.point) { // Opening a range
|
3747
3750
|
this.addActive(trackOpen);
|
3748
|
-
if (this.cursor.from < from && this.cursor.to > from)
|
3749
|
-
trackExtra++;
|
3750
3751
|
this.cursor.next();
|
3751
3752
|
}
|
3752
3753
|
else if (wasPoint && this.cursor.to == this.to && this.cursor.from < this.cursor.to) {
|
@@ -3759,8 +3760,6 @@
|
|
3759
3760
|
this.pointRank = this.cursor.rank;
|
3760
3761
|
this.to = this.cursor.to;
|
3761
3762
|
this.endSide = nextVal.endSide;
|
3762
|
-
if (this.cursor.from < from)
|
3763
|
-
trackExtra = 1;
|
3764
3763
|
this.cursor.next();
|
3765
3764
|
this.forward(this.to, this.endSide);
|
3766
3765
|
break;
|
@@ -3768,10 +3767,9 @@
|
|
3768
3767
|
}
|
3769
3768
|
}
|
3770
3769
|
if (trackOpen) {
|
3771
|
-
|
3772
|
-
|
3773
|
-
openStart++;
|
3774
|
-
this.openStart = openStart + trackExtra;
|
3770
|
+
this.openStart = 0;
|
3771
|
+
for (let i = trackOpen.length - 1; i >= 0 && trackOpen[i] < from; i--)
|
3772
|
+
this.openStart++;
|
3775
3773
|
}
|
3776
3774
|
}
|
3777
3775
|
activeForPoint(to) {
|
@@ -5826,7 +5824,7 @@
|
|
5826
5824
|
}
|
5827
5825
|
}
|
5828
5826
|
let take = Math.min(this.text.length - this.textOff, length, 512 /* T.Chunk */);
|
5829
|
-
this.flushBuffer(active.slice(
|
5827
|
+
this.flushBuffer(active.slice(active.length - openStart));
|
5830
5828
|
this.getLine().append(wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart);
|
5831
5829
|
this.atCursorPos = true;
|
5832
5830
|
this.textOff += take;
|
@@ -20367,6 +20365,8 @@
|
|
20367
20365
|
if (tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
|
20368
20366
|
!sameResults(active, this.active))
|
20369
20367
|
open = CompletionDialog.build(active, state, this.id, this.open, conf);
|
20368
|
+
else if (open && open.disabled && !active.some(a => a.state == 1 /* State.Pending */))
|
20369
|
+
open = null;
|
20370
20370
|
else if (open && tr.docChanged)
|
20371
20371
|
open = open.map(tr.changes);
|
20372
20372
|
if (!open && active.every(a => a.state != 1 /* State.Pending */) && active.some(a => a.hasResult()))
|
@@ -21292,7 +21292,7 @@
|
|
21292
21292
|
- F8: [`nextDiagnostic`](https://codemirror.net/6/docs/ref/#lint.nextDiagnostic)
|
21293
21293
|
*/
|
21294
21294
|
const lintKeymap = [
|
21295
|
-
{ key: "Mod-Shift-m", run: openLintPanel },
|
21295
|
+
{ key: "Mod-Shift-m", run: openLintPanel, preventDefault: true },
|
21296
21296
|
{ key: "F8", run: nextDiagnostic }
|
21297
21297
|
];
|
21298
21298
|
const lintPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
|
@@ -21916,10 +21916,10 @@
|
|
21916
21916
|
canShift(term) {
|
21917
21917
|
for (let sim = new SimulatedStack(this);;) {
|
21918
21918
|
let action = this.p.parser.stateSlot(sim.state, 4 /* DefaultReduce */) || this.p.parser.hasAction(sim.state, term);
|
21919
|
-
if ((action & 65536 /* ReduceFlag */) == 0)
|
21920
|
-
return true;
|
21921
21919
|
if (action == 0)
|
21922
21920
|
return false;
|
21921
|
+
if ((action & 65536 /* ReduceFlag */) == 0)
|
21922
|
+
return true;
|
21923
21923
|
sim.reduce(action);
|
21924
21924
|
}
|
21925
21925
|
}
|
@@ -23862,6 +23862,11 @@
|
|
23862
23862
|
|
23863
23863
|
/* global google */
|
23864
23864
|
|
23865
|
+
function unquoteSqlId (identifier) {
|
23866
|
+
const match = identifier.match(/^`(.*)`$/);
|
23867
|
+
return match ? match[1] : identifier
|
23868
|
+
}
|
23869
|
+
|
23865
23870
|
function init (parent, onSubmit, onShiftSubmit) {
|
23866
23871
|
addClickListener(document.getElementById('query-tab-button'), (event) => selectTab(event, 'query'));
|
23867
23872
|
addClickListener(document.getElementById('saved-tab-button'), (event) => selectTab(event, 'saved'));
|
@@ -23956,16 +23961,17 @@
|
|
23956
23961
|
schemas.forEach(([schemaName, schema]) => {
|
23957
23962
|
Object.entries(schema.tables).forEach(([tableName, table]) => {
|
23958
23963
|
const qualifiedTableName = schemas.length === 1 ? tableName : `${schemaName}.${tableName}`;
|
23964
|
+
const quotedQualifiedTableName = schemas.length === 1 ? `\`${tableName}\`` : `\`${schemaName}\`.\`${tableName}\``;
|
23959
23965
|
const columns = Object.keys(table.columns);
|
23960
23966
|
editorSchema[qualifiedTableName] = columns;
|
23961
|
-
const alias = window.metadata.table_aliases[
|
23967
|
+
const alias = window.metadata.table_aliases[qualifiedTableName];
|
23962
23968
|
if (alias) {
|
23963
23969
|
editorSchema[alias] = columns;
|
23964
23970
|
tables.push({
|
23965
23971
|
label: qualifiedTableName,
|
23966
23972
|
detail: alias,
|
23967
23973
|
alias_type: 'with',
|
23968
|
-
quoted:
|
23974
|
+
quoted: `${quotedQualifiedTableName} \`${alias}\``,
|
23969
23975
|
unquoted: `${qualifiedTableName} ${alias}`
|
23970
23976
|
});
|
23971
23977
|
tables.push({
|
@@ -24018,40 +24024,81 @@
|
|
24018
24024
|
schema: editorSchema,
|
24019
24025
|
tables
|
24020
24026
|
};
|
24021
|
-
const
|
24027
|
+
const originalSchemaCompletionSource = schemaCompletionSource(sqlConfig);
|
24028
|
+
const originalKeywordCompletionSource = keywordCompletionSource(MySQL, true);
|
24029
|
+
const keywordCompletions = [];
|
24030
|
+
window.metadata.joins.forEach((join) => {
|
24031
|
+
['JOIN', 'INNER JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'CROSS JOIN'].forEach((type) => {
|
24032
|
+
keywordCompletions.push({ label: `${type} ${join.label}`, apply: `${type} ${join.apply}`, type: 'keyword' });
|
24033
|
+
});
|
24034
|
+
});
|
24035
|
+
let combinedKeywordCompletionSource;
|
24036
|
+
if (keywordCompletions.length > 0) {
|
24037
|
+
const customKeywordCompletionSource = ifNotIn(['QuotedIdentifier', 'SpecialVar', 'String', 'LineComment', 'BlockComment', '.'], completeFromList(keywordCompletions));
|
24038
|
+
combinedKeywordCompletionSource = function (context) {
|
24039
|
+
const original = originalKeywordCompletionSource(context);
|
24040
|
+
const custom = customKeywordCompletionSource(context);
|
24041
|
+
if (original?.options && custom?.options) {
|
24042
|
+
original.options = original.options.concat(custom.options);
|
24043
|
+
}
|
24044
|
+
return original
|
24045
|
+
};
|
24046
|
+
} else {
|
24047
|
+
combinedKeywordCompletionSource = originalKeywordCompletionSource;
|
24048
|
+
}
|
24022
24049
|
const sqlExtension = new LanguageSupport(
|
24023
24050
|
MySQL.language,
|
24024
24051
|
[
|
24025
24052
|
MySQL.language.data.of({
|
24026
24053
|
autocomplete: (context) => {
|
24027
|
-
const result =
|
24054
|
+
const result = originalSchemaCompletionSource(context);
|
24028
24055
|
if (!hasTableAliases || !result?.options) return result
|
24029
24056
|
|
24030
24057
|
const tree = syntaxTree(context.state);
|
24031
24058
|
let node = tree.resolveInner(context.pos, -1);
|
24059
|
+
if (!node) return result
|
24032
24060
|
|
24033
24061
|
// We are trying to identify the case where we are autocompleting a table name after "from" or "join"
|
24062
|
+
|
24034
24063
|
// TODO: we don't handle the case where a user typed "select table.foo from". In that case we probably
|
24035
24064
|
// shouldn't autocomplete the alias. Though, if the user typed "select table.foo, t.bar", we won't know
|
24036
|
-
// what to do.
|
24065
|
+
// what to do. Maybe it is ok to force users to simply delete the alias after autocompleting.
|
24066
|
+
|
24037
24067
|
// TODO: if table aliases aren't enabled, we don't need to override autocomplete.
|
24038
24068
|
|
24039
|
-
|
24069
|
+
let foundSchema;
|
24070
|
+
if (node.name === 'Statement') {
|
24040
24071
|
// The node can be a Statement if the cursor is at the end of "from " and there is a complete
|
24041
24072
|
// statement in the editor (semicolon present). In that case we want to find the node just before the
|
24042
24073
|
// current position so that we can check whether it is "from" or "join".
|
24043
24074
|
node = node.childBefore(context.pos);
|
24044
|
-
} else if (node
|
24075
|
+
} else if (node.name === 'Script') {
|
24045
24076
|
// It seems the node can sometimes be a Script if the cursor is at the end of the last statement in the
|
24046
24077
|
// editor and the statement doesn't end in a semicolon. In that case we can find the last statement in the
|
24047
24078
|
// Script so that we can check whether it is "from" or "join".
|
24048
24079
|
node = node.lastChild?.childBefore(context.pos);
|
24049
|
-
} else if (['Identifier', 'QuotedIdentifier', 'Keyword'].includes(node
|
24080
|
+
} else if (['Identifier', 'QuotedIdentifier', 'Keyword', '.'].includes(node.name)) {
|
24050
24081
|
// If the node is an Identifier, we might be in the middle of typing the table name. If the node is a
|
24051
24082
|
// Keyword but isn't "from" or "join", we might be in the middle of typing a table name that is similar
|
24052
24083
|
// to a Keyword, for instance "orders" or "selections" or "fromages". In these cases, look for the previous
|
24053
|
-
// sibling so that we can check whether it is "from" or "join".
|
24054
|
-
|
24084
|
+
// sibling so that we can check whether it is "from" or "join". If we found a '.' or if the previous
|
24085
|
+
// sibling is a '.', we might be in the middle of typing something like "schema.table" or
|
24086
|
+
// "`schema`.table" or "`schema`.`table`". In these cases we need to record the schema used so that we
|
24087
|
+
// can autocomplete table names with aliases.
|
24088
|
+
if (node.name !== '.') {
|
24089
|
+
node = node.prevSibling;
|
24090
|
+
}
|
24091
|
+
|
24092
|
+
if (node?.name === '.') {
|
24093
|
+
node = node.prevSibling;
|
24094
|
+
if (['Identifier', 'QuotedIdentifier'].includes(node?.name)) {
|
24095
|
+
foundSchema = unquoteSqlId(context.state.doc.sliceString(node.from, node.to));
|
24096
|
+
node = node.parent;
|
24097
|
+
if (node?.name === 'CompositeIdentifier') {
|
24098
|
+
node = node.prevSibling;
|
24099
|
+
}
|
24100
|
+
}
|
24101
|
+
}
|
24055
24102
|
}
|
24056
24103
|
|
24057
24104
|
const nodeText = node ? context.state.doc.sliceString(node.from, node.to).toLowerCase() : null;
|
@@ -24076,13 +24123,21 @@
|
|
24076
24123
|
option.apply = option.unquoted;
|
24077
24124
|
}
|
24078
24125
|
}
|
24126
|
+
if (foundSchema) {
|
24127
|
+
const unquotedLabel = unquoteSqlId(option.label);
|
24128
|
+
const quoted = unquotedLabel !== option.label;
|
24129
|
+
const alias = window.metadata.table_aliases[`${foundSchema}.${unquotedLabel}`];
|
24130
|
+
if (alias) {
|
24131
|
+
option = { label: quoted ? `\`${unquotedLabel}\` \`${alias}\`` : `${option.label} ${alias}` };
|
24132
|
+
}
|
24133
|
+
}
|
24079
24134
|
return option
|
24080
24135
|
});
|
24081
24136
|
return result
|
24082
24137
|
}
|
24083
24138
|
}),
|
24084
24139
|
MySQL.language.data.of({
|
24085
|
-
autocomplete:
|
24140
|
+
autocomplete: combinedKeywordCompletionSource
|
24086
24141
|
})
|
24087
24142
|
]
|
24088
24143
|
);
|