sqlui 0.1.40 → 0.1.41
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/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
|
);
|