@arcgis/coding-components 4.31.0-next.21 → 4.31.0-next.23
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.
- package/dist/arcgis-coding-components/arcgis-coding-components.esm.js +2 -2
- package/dist/arcgis-coding-components/assets/code-editor/sql-expr.worker.js +433 -22
- package/dist/arcgis-coding-components/index.esm.js +2 -2
- package/dist/arcgis-coding-components/p-08b8fd1f.js +6 -0
- package/dist/arcgis-coding-components/{p-cdc52d2f.js → p-1402a8f3.js} +1 -1
- package/dist/arcgis-coding-components/{p-8eb7e694.js → p-1635ee06.js} +2 -2
- package/dist/arcgis-coding-components/{p-57b8bcd7.js → p-1d45a6dd.js} +1 -1
- package/dist/arcgis-coding-components/{p-5b67b62b.js → p-22c2d840.js} +2 -2
- package/dist/arcgis-coding-components/{p-cbee273f.js → p-2cfa8f19.js} +2 -2
- package/dist/arcgis-coding-components/{p-ffce0588.entry.js → p-3f1b5f73.entry.js} +2 -2
- package/dist/arcgis-coding-components/{p-a2341e0c.js → p-50e53c02.js} +2 -2
- package/dist/arcgis-coding-components/{p-1b799777.js → p-5872baf9.js} +2 -2
- package/dist/arcgis-coding-components/{p-d7d1f130.js → p-776d550d.js} +9 -9
- package/dist/arcgis-coding-components/{p-4d5cf512.entry.js → p-9c58968d.entry.js} +2 -2
- package/dist/arcgis-coding-components/{p-b715834b.js → p-9c7cef40.js} +2 -2
- package/dist/arcgis-coding-components/{p-6621a80f.js → p-9e1c9066.js} +2 -2
- package/dist/arcgis-coding-components/{p-56266a64.js → p-9edc9c4b.js} +2 -2
- package/dist/arcgis-coding-components/{p-64137fec.js → p-a444264e.js} +2 -2
- package/dist/arcgis-coding-components/{p-e5a924a1.js → p-b4912aa0.js} +1 -1
- package/dist/arcgis-coding-components/{p-f2d64a9d.js → p-c4521d1d.js} +2 -2
- package/dist/arcgis-coding-components/{p-e1f9b463.entry.js → p-d99efb76.entry.js} +2 -2
- package/dist/arcgis-coding-components/{p-ff62d134.js → p-f18cfc50.js} +2 -2
- package/dist/cjs/{app-globals-a9ef3ca8.js → app-globals-b0c856f6.js} +1 -1
- package/dist/cjs/{arcade-defaults-f5241680.js → arcade-defaults-25d38bc0.js} +4 -4
- package/dist/cjs/{arcade-language-features-0b9f3947.js → arcade-language-features-0422f79b.js} +2 -2
- package/dist/cjs/{arcade-mode-cc08d9cd.js → arcade-mode-151f7044.js} +3 -3
- package/dist/cjs/arcgis-arcade-editor_6.cjs.entry.js +5 -5
- package/dist/cjs/arcgis-coding-components.cjs.js +3 -3
- package/dist/cjs/arcgis-sql-expression-editor.cjs.entry.js +4 -4
- package/dist/cjs/arcgis-sql-expression-fields.cjs.entry.js +4 -4
- package/dist/cjs/{css-bb6a49ec.js → css-c3bfee00.js} +1 -1
- package/dist/cjs/{cssMode-e7fac7d7.js → cssMode-c7d6c64b.js} +2 -2
- package/dist/cjs/{html-7ff4071d.js → html-3ff5bd3b.js} +2 -2
- package/dist/cjs/{htmlMode-c89193ec.js → htmlMode-71714c3e.js} +2 -2
- package/dist/cjs/{index-4b7880ab.js → index-d386d3e2.js} +1 -1
- package/dist/cjs/index.cjs.js +4 -4
- package/dist/cjs/{javascript-864c0220.js → javascript-d1a20c9f.js} +3 -3
- package/dist/cjs/{jsonMode-ac4a7be7.js → jsonMode-2b25e5f4.js} +2 -2
- package/dist/cjs/{language-defaults-base-57b37f9f.js → language-defaults-base-fe1d850e.js} +29 -9
- package/dist/cjs/loader.cjs.js +3 -3
- package/dist/cjs/{sql-expr-defaults-245b036d.js → sql-expr-defaults-0acd5dfe.js} +4 -4
- package/dist/cjs/sql-expr-mode-bc156416.js +1825 -0
- package/dist/cjs/{tsMode-d30d4ab1.js → tsMode-9cfe75b9.js} +2 -2
- package/dist/cjs/{typescript-6c87ddb3.js → typescript-fc9b2e83.js} +2 -2
- package/dist/components/arcade-defaults.js +1 -1
- package/dist/components/arcade-language-features.js +1 -1
- package/dist/components/arcade-mode.js +1 -1
- package/dist/components/arcade-results.js +1 -1
- package/dist/components/arcade-suggestions.js +1 -1
- package/dist/components/arcade-variables.js +1 -1
- package/dist/components/arcgis-arcade-editor.js +1 -1
- package/dist/components/arcgis-arcade-results.js +1 -1
- package/dist/components/arcgis-arcade-suggestions.js +1 -1
- package/dist/components/arcgis-arcade-variables.js +1 -1
- package/dist/components/arcgis-assets.d.ts +1 -1
- package/dist/components/arcgis-assets.js +1 -1
- package/dist/components/arcgis-code-editor.js +1 -1
- package/dist/components/arcgis-language-api-panel.js +1 -1
- package/dist/components/arcgis-sql-expression-editor.js +1 -1
- package/dist/components/arcgis-sql-expression-fields.js +1 -1
- package/dist/components/chunk-2JTWBRMN.js +1 -1
- package/dist/components/code-editor.js +1 -1
- package/dist/components/fields.js +20 -2
- package/dist/components/index.js +1 -1
- package/dist/components/index2.js +1 -1
- package/dist/components/language-api-panel.js +1 -1
- package/dist/components/language-defaults-base.js +1 -1
- package/dist/components/markdown.js +1 -1
- package/dist/components/sql-expr-defaults.js +1 -1
- package/dist/components/sql-expr-mode.js +33 -19051
- package/dist/components/sql-expression-fields.js +1 -1
- package/dist/components/useT9n.js +1 -1
- package/dist/components/utilities.js +1 -1
- package/dist/esm/{app-globals-6d0ca11d.js → app-globals-1d6b10d3.js} +1 -1
- package/dist/esm/{arcade-defaults-066445c4.js → arcade-defaults-059a08f1.js} +4 -4
- package/dist/esm/{arcade-language-features-0e00c199.js → arcade-language-features-749f495a.js} +2 -2
- package/dist/esm/{arcade-mode-abf1e1cf.js → arcade-mode-5e2da07e.js} +3 -3
- package/dist/esm/arcgis-arcade-editor_6.entry.js +5 -5
- package/dist/esm/arcgis-coding-components.js +4 -4
- package/dist/esm/arcgis-sql-expression-editor.entry.js +4 -4
- package/dist/esm/arcgis-sql-expression-fields.entry.js +4 -4
- package/dist/esm/{css-c6dae12d.js → css-0fa06f45.js} +1 -1
- package/dist/esm/{cssMode-052bb603.js → cssMode-7d51359f.js} +2 -2
- package/dist/esm/{html-572696a1.js → html-900c5e2a.js} +2 -2
- package/dist/esm/{htmlMode-fc184f2d.js → htmlMode-aaba1915.js} +2 -2
- package/dist/esm/{index-0edd9846.js → index-51f840b4.js} +1 -1
- package/dist/esm/index.js +4 -4
- package/dist/esm/{javascript-8bfc0096.js → javascript-4a0ddd5a.js} +3 -3
- package/dist/esm/{jsonMode-7adf94ff.js → jsonMode-bcfe66f5.js} +2 -2
- package/dist/esm/{language-defaults-base-85a7f476.js → language-defaults-base-5cb37b1e.js} +28 -10
- package/dist/esm/loader.js +4 -4
- package/dist/esm/{sql-expr-defaults-be84ec7f.js → sql-expr-defaults-f1fd7291.js} +4 -4
- package/dist/esm/sql-expr-mode-0f087c3e.js +1821 -0
- package/dist/esm/{tsMode-d10773c8.js → tsMode-6deb8933.js} +2 -2
- package/dist/esm/{typescript-9491f23e.js → typescript-11a558e0.js} +2 -2
- package/dist/loader/cdn.js +1 -1
- package/dist/loader/index.cjs.js +1 -1
- package/dist/loader/index.es2017.js +1 -1
- package/dist/loader/index.js +1 -1
- package/dist/types/utils/sql-expr-monaco/sql-expr-completion.d.ts +1 -1
- package/dist/types/utils/sql-expr-monaco/sql-expr-validation-utils.d.ts +7 -8
- package/dist/types/utils/sql-expr-monaco/sql-expr-validation.d.ts +2 -3
- package/dist/types/utils/sql-expr-monaco/types.d.ts +2 -6
- package/package.json +13 -14
- package/dist/arcgis-coding-components/p-77dd5521.js +0 -6
- package/dist/cjs/sql-expr-mode-304f5ce2.js +0 -20844
- package/dist/esm/sql-expr-mode-a4413e5c.js +0 -20840
- package/dist/types/utils/sql-expr-monaco/DependentFiles/DateOnly.d.ts +0 -41
- package/dist/types/utils/sql-expr-monaco/DependentFiles/SqlInterval.d.ts +0 -16
- package/dist/types/utils/sql-expr-monaco/DependentFiles/SqlTimestampOffset.d.ts +0 -26
- package/dist/types/utils/sql-expr-monaco/DependentFiles/TimeOnly.d.ts +0 -37
- package/dist/types/utils/sql-expr-monaco/DependentFiles/UnknownTimeZone.d.ts +0 -11
- package/dist/types/utils/sql-expr-monaco/DependentFiles/WhereGrammar.d.ts +0 -122
- package/dist/types/utils/sql-expr-monaco/DependentFiles/sqlCompareUtils.d.ts +0 -5
- package/dist/types/utils/sql-expr-monaco/DependentFiles/sqlDateParsingUtils.d.ts +0 -18
- package/dist/types/utils/sql-expr-monaco/DependentFiles/sqlUtils.d.ts +0 -6
- package/dist/types/utils/sql-expr-monaco/DependentFiles/standardizedFunctions.d.ts +0 -156
- package/dist/types/utils/sql-expr-monaco/DependentFiles/support.d.ts +0 -150
- package/dist/types/utils/sql-expr-monaco/PeggyGrammar/sql92grammar.d.ts +0 -1397
|
@@ -0,0 +1,1821 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* All material copyright Esri, All Rights Reserved, unless otherwise specified.
|
|
3
|
+
* See https://js.arcgis.com/4.31/esri/copyright.txt for details.
|
|
4
|
+
* v4.31.0-next.23
|
|
5
|
+
*/
|
|
6
|
+
import { g as editor, T as importCoreSql, V as newLayersSupportFieldsIndex, z as languages, m as main, Q as debounce, O as Emitter } from './language-defaults-base-5cb37b1e.js';
|
|
7
|
+
import { b as getFieldsFromLayerVariable, s as sqlExprDefaults } from './sql-expr-defaults-f1fd7291.js';
|
|
8
|
+
import { t as toCompletionItemKind } from './arcade-language-features-749f495a.js';
|
|
9
|
+
import './index-51f840b4.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Manager to create our sql-expression worker and client proxy
|
|
13
|
+
*/
|
|
14
|
+
class SqlExprWorkerManager {
|
|
15
|
+
constructor(_defaults) {
|
|
16
|
+
this._defaults = _defaults;
|
|
17
|
+
this._worker = null;
|
|
18
|
+
this._client = null;
|
|
19
|
+
// Observes the sql-expression defaults. If modified then stop the worker.
|
|
20
|
+
this._configChangeListener = this._defaults.onDidChange(() => this.stopWorker());
|
|
21
|
+
}
|
|
22
|
+
dispose() {
|
|
23
|
+
this._configChangeListener.dispose();
|
|
24
|
+
this.stopWorker();
|
|
25
|
+
}
|
|
26
|
+
stopWorker() {
|
|
27
|
+
if (this._worker) {
|
|
28
|
+
this._worker.dispose();
|
|
29
|
+
this._worker = null;
|
|
30
|
+
}
|
|
31
|
+
this._client = null;
|
|
32
|
+
}
|
|
33
|
+
async _getClientProxy() {
|
|
34
|
+
// We used createWebWorker to create or run the web worker if it isn’t already created.
|
|
35
|
+
// Otherwise, we get and return the proxy client.
|
|
36
|
+
if (!this._client) {
|
|
37
|
+
const { languageId, languageOptions } = this._defaults;
|
|
38
|
+
this._worker = editor.createWebWorker({
|
|
39
|
+
moduleId: "SqlExprWorker",
|
|
40
|
+
label: languageId,
|
|
41
|
+
createData: {
|
|
42
|
+
languageId,
|
|
43
|
+
languageOptions,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
this._client = this._worker.getProxy();
|
|
47
|
+
}
|
|
48
|
+
return await this._client;
|
|
49
|
+
}
|
|
50
|
+
async getLanguageServiceWorker(...resources) {
|
|
51
|
+
const _client = await this._getClientProxy();
|
|
52
|
+
await this._worker?.withSyncedResources(resources);
|
|
53
|
+
return _client;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const sqlExprLanguageConfig = {
|
|
58
|
+
comments: {
|
|
59
|
+
lineComment: "--",
|
|
60
|
+
blockComment: ["/*", "*/"],
|
|
61
|
+
},
|
|
62
|
+
brackets: [
|
|
63
|
+
["{", "}"],
|
|
64
|
+
["[", "]"],
|
|
65
|
+
["(", ")"],
|
|
66
|
+
],
|
|
67
|
+
autoClosingPairs: [
|
|
68
|
+
{ open: "{", close: "}" },
|
|
69
|
+
{ open: "[", close: "]" },
|
|
70
|
+
{ open: "(", close: ")" },
|
|
71
|
+
{ open: '"', close: '"' },
|
|
72
|
+
{ open: "'", close: "'" },
|
|
73
|
+
],
|
|
74
|
+
surroundingPairs: [
|
|
75
|
+
{ open: "{", close: "}" },
|
|
76
|
+
{ open: "[", close: "]" },
|
|
77
|
+
{ open: "(", close: ")" },
|
|
78
|
+
{ open: '"', close: '"' },
|
|
79
|
+
{ open: "'", close: "'" },
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
// TODO: trim down
|
|
83
|
+
const sqlExprLanguageSyntax = {
|
|
84
|
+
defaultToken: "",
|
|
85
|
+
tokenPostfix: ".sql",
|
|
86
|
+
ignoreCase: true,
|
|
87
|
+
brackets: [
|
|
88
|
+
{ open: "[", close: "]", token: "delimiter.square" },
|
|
89
|
+
{ open: "(", close: ")", token: "delimiter.parenthesis" },
|
|
90
|
+
],
|
|
91
|
+
keywords: [
|
|
92
|
+
// This list is generated using `keywords.js`
|
|
93
|
+
"ABORT",
|
|
94
|
+
"ABSOLUTE",
|
|
95
|
+
"ACTION",
|
|
96
|
+
"ADA",
|
|
97
|
+
"ADD",
|
|
98
|
+
"AFTER",
|
|
99
|
+
"ALL",
|
|
100
|
+
"ALLOCATE",
|
|
101
|
+
"ALTER",
|
|
102
|
+
"ALWAYS",
|
|
103
|
+
"ANALYZE",
|
|
104
|
+
"AND",
|
|
105
|
+
"ANY",
|
|
106
|
+
"ARE",
|
|
107
|
+
"AS",
|
|
108
|
+
"ASC",
|
|
109
|
+
"ASSERTION",
|
|
110
|
+
"AT",
|
|
111
|
+
"ATTACH",
|
|
112
|
+
"AUTHORIZATION",
|
|
113
|
+
"AUTOINCREMENT",
|
|
114
|
+
"AVG",
|
|
115
|
+
"BACKUP",
|
|
116
|
+
"BEFORE",
|
|
117
|
+
"BEGIN",
|
|
118
|
+
"BETWEEN",
|
|
119
|
+
"BIT",
|
|
120
|
+
"BIT_LENGTH",
|
|
121
|
+
"BOTH",
|
|
122
|
+
"BREAK",
|
|
123
|
+
"BROWSE",
|
|
124
|
+
"BULK",
|
|
125
|
+
"BY",
|
|
126
|
+
"CASCADE",
|
|
127
|
+
"CASCADED",
|
|
128
|
+
"CASE",
|
|
129
|
+
"CAST",
|
|
130
|
+
"CATALOG",
|
|
131
|
+
"CHAR",
|
|
132
|
+
"CHARACTER",
|
|
133
|
+
"CHARACTER_LENGTH",
|
|
134
|
+
"CHAR_LENGTH",
|
|
135
|
+
"CHECK",
|
|
136
|
+
"CHECKPOINT",
|
|
137
|
+
"CLOSE",
|
|
138
|
+
"CLUSTERED",
|
|
139
|
+
"COALESCE",
|
|
140
|
+
"COLLATE",
|
|
141
|
+
"COLLATION",
|
|
142
|
+
"COLUMN",
|
|
143
|
+
"COMMIT",
|
|
144
|
+
"COMPUTE",
|
|
145
|
+
"CONFLICT",
|
|
146
|
+
"CONNECT",
|
|
147
|
+
"CONNECTION",
|
|
148
|
+
"CONSTRAINT",
|
|
149
|
+
"CONSTRAINTS",
|
|
150
|
+
"CONTAINS",
|
|
151
|
+
"CONTAINSTABLE",
|
|
152
|
+
"CONTINUE",
|
|
153
|
+
"CONVERT",
|
|
154
|
+
"CORRESPONDING",
|
|
155
|
+
"COUNT",
|
|
156
|
+
"CREATE",
|
|
157
|
+
"CROSS",
|
|
158
|
+
"CURRENT",
|
|
159
|
+
"CURRENT_DATE",
|
|
160
|
+
"CURRENT_TIME",
|
|
161
|
+
"CURRENT_TIMESTAMP",
|
|
162
|
+
"CURRENT_USER",
|
|
163
|
+
"CURSOR",
|
|
164
|
+
"DATABASE",
|
|
165
|
+
"DATE",
|
|
166
|
+
"DAY",
|
|
167
|
+
"DBCC",
|
|
168
|
+
"DEALLOCATE",
|
|
169
|
+
"DEC",
|
|
170
|
+
"DECIMAL",
|
|
171
|
+
"DECLARE",
|
|
172
|
+
"DEFAULT",
|
|
173
|
+
"DEFERRABLE",
|
|
174
|
+
"DEFERRED",
|
|
175
|
+
"DELETE",
|
|
176
|
+
"DENY",
|
|
177
|
+
"DESC",
|
|
178
|
+
"DESCRIBE",
|
|
179
|
+
"DESCRIPTOR",
|
|
180
|
+
"DETACH",
|
|
181
|
+
"DIAGNOSTICS",
|
|
182
|
+
"DISCONNECT",
|
|
183
|
+
"DISK",
|
|
184
|
+
"DISTINCT",
|
|
185
|
+
"DISTRIBUTED",
|
|
186
|
+
"DO",
|
|
187
|
+
"DOMAIN",
|
|
188
|
+
"DOUBLE",
|
|
189
|
+
"DROP",
|
|
190
|
+
"DUMP",
|
|
191
|
+
"EACH",
|
|
192
|
+
"ELSE",
|
|
193
|
+
"END",
|
|
194
|
+
"END-EXEC",
|
|
195
|
+
"ERRLVL",
|
|
196
|
+
"ESCAPE",
|
|
197
|
+
"EXCEPT",
|
|
198
|
+
"EXCEPTION",
|
|
199
|
+
"EXCLUDE",
|
|
200
|
+
"EXCLUSIVE",
|
|
201
|
+
"EXEC",
|
|
202
|
+
"EXECUTE",
|
|
203
|
+
"EXISTS",
|
|
204
|
+
"EXIT",
|
|
205
|
+
"EXPLAIN",
|
|
206
|
+
"EXTERNAL",
|
|
207
|
+
"EXTRACT",
|
|
208
|
+
"FAIL",
|
|
209
|
+
"FALSE",
|
|
210
|
+
"FETCH",
|
|
211
|
+
"FILE",
|
|
212
|
+
"FILLFACTOR",
|
|
213
|
+
"FILTER",
|
|
214
|
+
"FIRST",
|
|
215
|
+
"FLOAT",
|
|
216
|
+
"FOLLOWING",
|
|
217
|
+
"FOR",
|
|
218
|
+
"FOREIGN",
|
|
219
|
+
"FORTRAN",
|
|
220
|
+
"FOUND",
|
|
221
|
+
"FREETEXT",
|
|
222
|
+
"FREETEXTTABLE",
|
|
223
|
+
"FROM",
|
|
224
|
+
"FULL",
|
|
225
|
+
"FUNCTION",
|
|
226
|
+
"GENERATED",
|
|
227
|
+
"GET",
|
|
228
|
+
"GLOB",
|
|
229
|
+
"GLOBAL",
|
|
230
|
+
"GO",
|
|
231
|
+
"GOTO",
|
|
232
|
+
"GRANT",
|
|
233
|
+
"GROUP",
|
|
234
|
+
"GROUPS",
|
|
235
|
+
"HAVING",
|
|
236
|
+
"HOLDLOCK",
|
|
237
|
+
"HOUR",
|
|
238
|
+
"IDENTITY",
|
|
239
|
+
"IDENTITYCOL",
|
|
240
|
+
"IDENTITY_INSERT",
|
|
241
|
+
"IF",
|
|
242
|
+
"IGNORE",
|
|
243
|
+
"IMMEDIATE",
|
|
244
|
+
"IN",
|
|
245
|
+
"INCLUDE",
|
|
246
|
+
"INDEX",
|
|
247
|
+
"INDEXED",
|
|
248
|
+
"INDICATOR",
|
|
249
|
+
"INITIALLY",
|
|
250
|
+
"INNER",
|
|
251
|
+
"INPUT",
|
|
252
|
+
"INSENSITIVE",
|
|
253
|
+
"INSERT",
|
|
254
|
+
"INSTEAD",
|
|
255
|
+
"INT",
|
|
256
|
+
"INTEGER",
|
|
257
|
+
"INTERSECT",
|
|
258
|
+
"INTERVAL",
|
|
259
|
+
"INTO",
|
|
260
|
+
"IS",
|
|
261
|
+
"ISNULL",
|
|
262
|
+
"ISOLATION",
|
|
263
|
+
"JOIN",
|
|
264
|
+
"KEY",
|
|
265
|
+
"KILL",
|
|
266
|
+
"LANGUAGE",
|
|
267
|
+
"LAST",
|
|
268
|
+
"LEADING",
|
|
269
|
+
"LEFT",
|
|
270
|
+
"LEVEL",
|
|
271
|
+
"LIKE",
|
|
272
|
+
"LIMIT",
|
|
273
|
+
"LINENO",
|
|
274
|
+
"LOAD",
|
|
275
|
+
"LOCAL",
|
|
276
|
+
"LOWER",
|
|
277
|
+
"MATCH",
|
|
278
|
+
"MATERIALIZED",
|
|
279
|
+
"MAX",
|
|
280
|
+
"MERGE",
|
|
281
|
+
"MIN",
|
|
282
|
+
"MINUTE",
|
|
283
|
+
"MODULE",
|
|
284
|
+
"MONTH",
|
|
285
|
+
"NAMES",
|
|
286
|
+
"NATIONAL",
|
|
287
|
+
"NATURAL",
|
|
288
|
+
"NCHAR",
|
|
289
|
+
"NEXT",
|
|
290
|
+
"NO",
|
|
291
|
+
"NOCHECK",
|
|
292
|
+
"NONCLUSTERED",
|
|
293
|
+
"NONE",
|
|
294
|
+
"NOT",
|
|
295
|
+
"NOTHING",
|
|
296
|
+
"NOTNULL",
|
|
297
|
+
"NULL",
|
|
298
|
+
"NULLIF",
|
|
299
|
+
"NULLS",
|
|
300
|
+
"NUMERIC",
|
|
301
|
+
"OCTET_LENGTH",
|
|
302
|
+
"OF",
|
|
303
|
+
"OFF",
|
|
304
|
+
"OFFSET",
|
|
305
|
+
"OFFSETS",
|
|
306
|
+
"ON",
|
|
307
|
+
"ONLY",
|
|
308
|
+
"OPEN",
|
|
309
|
+
"OPENDATASOURCE",
|
|
310
|
+
"OPENQUERY",
|
|
311
|
+
"OPENROWSET",
|
|
312
|
+
"OPENXML",
|
|
313
|
+
"OPTION",
|
|
314
|
+
"OR",
|
|
315
|
+
"ORDER",
|
|
316
|
+
"OTHERS",
|
|
317
|
+
"OUTER",
|
|
318
|
+
"OUTPUT",
|
|
319
|
+
"OVER",
|
|
320
|
+
"OVERLAPS",
|
|
321
|
+
"PAD",
|
|
322
|
+
"PARTIAL",
|
|
323
|
+
"PARTITION",
|
|
324
|
+
"PASCAL",
|
|
325
|
+
"PERCENT",
|
|
326
|
+
"PIVOT",
|
|
327
|
+
"PLAN",
|
|
328
|
+
"POSITION",
|
|
329
|
+
"PRAGMA",
|
|
330
|
+
"PRECEDING",
|
|
331
|
+
"PRECISION",
|
|
332
|
+
"PREPARE",
|
|
333
|
+
"PRESERVE",
|
|
334
|
+
"PRIMARY",
|
|
335
|
+
"PRINT",
|
|
336
|
+
"PRIOR",
|
|
337
|
+
"PRIVILEGES",
|
|
338
|
+
"PROC",
|
|
339
|
+
"PROCEDURE",
|
|
340
|
+
"PUBLIC",
|
|
341
|
+
"QUERY",
|
|
342
|
+
"RAISE",
|
|
343
|
+
"RAISERROR",
|
|
344
|
+
"RANGE",
|
|
345
|
+
"READ",
|
|
346
|
+
"READTEXT",
|
|
347
|
+
"REAL",
|
|
348
|
+
"RECONFIGURE",
|
|
349
|
+
"RECURSIVE",
|
|
350
|
+
"REFERENCES",
|
|
351
|
+
"REGEXP",
|
|
352
|
+
"REINDEX",
|
|
353
|
+
"RELATIVE",
|
|
354
|
+
"RELEASE",
|
|
355
|
+
"RENAME",
|
|
356
|
+
"REPLACE",
|
|
357
|
+
"REPLICATION",
|
|
358
|
+
"RESTORE",
|
|
359
|
+
"RESTRICT",
|
|
360
|
+
"RETURN",
|
|
361
|
+
"RETURNING",
|
|
362
|
+
"REVERT",
|
|
363
|
+
"REVOKE",
|
|
364
|
+
"RIGHT",
|
|
365
|
+
"ROLLBACK",
|
|
366
|
+
"ROW",
|
|
367
|
+
"ROWCOUNT",
|
|
368
|
+
"ROWGUIDCOL",
|
|
369
|
+
"ROWS",
|
|
370
|
+
"RULE",
|
|
371
|
+
"SAVE",
|
|
372
|
+
"SAVEPOINT",
|
|
373
|
+
"SCHEMA",
|
|
374
|
+
"SCROLL",
|
|
375
|
+
"SECOND",
|
|
376
|
+
"SECTION",
|
|
377
|
+
"SECURITYAUDIT",
|
|
378
|
+
"SELECT",
|
|
379
|
+
"SEMANTICKEYPHRASETABLE",
|
|
380
|
+
"SEMANTICSIMILARITYDETAILSTABLE",
|
|
381
|
+
"SEMANTICSIMILARITYTABLE",
|
|
382
|
+
"SESSION",
|
|
383
|
+
"SESSION_USER",
|
|
384
|
+
"SET",
|
|
385
|
+
"SETUSER",
|
|
386
|
+
"SHUTDOWN",
|
|
387
|
+
"SIZE",
|
|
388
|
+
"SMALLINT",
|
|
389
|
+
"SOME",
|
|
390
|
+
"SPACE",
|
|
391
|
+
"SQL",
|
|
392
|
+
"SQLCA",
|
|
393
|
+
"SQLCODE",
|
|
394
|
+
"SQLERROR",
|
|
395
|
+
"SQLSTATE",
|
|
396
|
+
"SQLWARNING",
|
|
397
|
+
"STATISTICS",
|
|
398
|
+
"SUBSTRING",
|
|
399
|
+
"SUM",
|
|
400
|
+
"SYSTEM_USER",
|
|
401
|
+
"TABLE",
|
|
402
|
+
"TABLESAMPLE",
|
|
403
|
+
"TEMP",
|
|
404
|
+
"TEMPORARY",
|
|
405
|
+
"TEXTSIZE",
|
|
406
|
+
"THEN",
|
|
407
|
+
"TIES",
|
|
408
|
+
"TIME",
|
|
409
|
+
"TIMESTAMP",
|
|
410
|
+
"TIMEZONE_HOUR",
|
|
411
|
+
"TIMEZONE_MINUTE",
|
|
412
|
+
"TO",
|
|
413
|
+
"TOP",
|
|
414
|
+
"TRAILING",
|
|
415
|
+
"TRAN",
|
|
416
|
+
"TRANSACTION",
|
|
417
|
+
"TRANSLATE",
|
|
418
|
+
"TRANSLATION",
|
|
419
|
+
"TRIGGER",
|
|
420
|
+
"TRIM",
|
|
421
|
+
"TRUE",
|
|
422
|
+
"TRUNCATE",
|
|
423
|
+
"TRY_CONVERT",
|
|
424
|
+
"TSEQUAL",
|
|
425
|
+
"UNBOUNDED",
|
|
426
|
+
"UNION",
|
|
427
|
+
"UNIQUE",
|
|
428
|
+
"UNKNOWN",
|
|
429
|
+
"UNPIVOT",
|
|
430
|
+
"UPDATE",
|
|
431
|
+
"UPDATETEXT",
|
|
432
|
+
"UPPER",
|
|
433
|
+
"USAGE",
|
|
434
|
+
"USE",
|
|
435
|
+
"USER",
|
|
436
|
+
"USING",
|
|
437
|
+
"VACUUM",
|
|
438
|
+
"VALUE",
|
|
439
|
+
"VALUES",
|
|
440
|
+
"VARCHAR",
|
|
441
|
+
"VARYING",
|
|
442
|
+
"VIEW",
|
|
443
|
+
"VIRTUAL",
|
|
444
|
+
"WAITFOR",
|
|
445
|
+
"WHEN",
|
|
446
|
+
"WHENEVER",
|
|
447
|
+
"WHERE",
|
|
448
|
+
"WHILE",
|
|
449
|
+
"WINDOW",
|
|
450
|
+
"WITH",
|
|
451
|
+
"WITHIN GROUP",
|
|
452
|
+
"WITHOUT",
|
|
453
|
+
"WORK",
|
|
454
|
+
"WRITE",
|
|
455
|
+
"WRITETEXT",
|
|
456
|
+
"YEAR",
|
|
457
|
+
"ZONE",
|
|
458
|
+
],
|
|
459
|
+
operators: [
|
|
460
|
+
// Logical
|
|
461
|
+
"ALL",
|
|
462
|
+
"AND",
|
|
463
|
+
"ANY",
|
|
464
|
+
"BETWEEN",
|
|
465
|
+
"EXISTS",
|
|
466
|
+
"IN",
|
|
467
|
+
"LIKE",
|
|
468
|
+
"NOT",
|
|
469
|
+
"OR",
|
|
470
|
+
"SOME",
|
|
471
|
+
// Set
|
|
472
|
+
"EXCEPT",
|
|
473
|
+
"INTERSECT",
|
|
474
|
+
"UNION",
|
|
475
|
+
// Join
|
|
476
|
+
"APPLY",
|
|
477
|
+
"CROSS",
|
|
478
|
+
"FULL",
|
|
479
|
+
"INNER",
|
|
480
|
+
"JOIN",
|
|
481
|
+
"LEFT",
|
|
482
|
+
"OUTER",
|
|
483
|
+
"RIGHT",
|
|
484
|
+
// Predicates
|
|
485
|
+
"CONTAINS",
|
|
486
|
+
"FREETEXT",
|
|
487
|
+
"IS",
|
|
488
|
+
"NULL",
|
|
489
|
+
// Pivoting
|
|
490
|
+
"PIVOT",
|
|
491
|
+
"UNPIVOT",
|
|
492
|
+
// Merging
|
|
493
|
+
"MATCHED",
|
|
494
|
+
],
|
|
495
|
+
builtinFunctions: [
|
|
496
|
+
// Aggregate
|
|
497
|
+
"AVG",
|
|
498
|
+
"CHECKSUM_AGG",
|
|
499
|
+
"COUNT",
|
|
500
|
+
"COUNT_BIG",
|
|
501
|
+
"GROUPING",
|
|
502
|
+
"GROUPING_ID",
|
|
503
|
+
"MAX",
|
|
504
|
+
"MIN",
|
|
505
|
+
"SUM",
|
|
506
|
+
"STDEV",
|
|
507
|
+
"STDEVP",
|
|
508
|
+
"VAR",
|
|
509
|
+
"VARP",
|
|
510
|
+
// Analytic
|
|
511
|
+
"CUME_DIST",
|
|
512
|
+
"FIRST_VALUE",
|
|
513
|
+
"LAG",
|
|
514
|
+
"LAST_VALUE",
|
|
515
|
+
"LEAD",
|
|
516
|
+
"PERCENTILE_CONT",
|
|
517
|
+
"PERCENTILE_DISC",
|
|
518
|
+
"PERCENT_RANK",
|
|
519
|
+
// Collation
|
|
520
|
+
"COLLATE",
|
|
521
|
+
"COLLATIONPROPERTY",
|
|
522
|
+
"TERTIARY_WEIGHTS",
|
|
523
|
+
// Azure
|
|
524
|
+
"FEDERATION_FILTERING_VALUE",
|
|
525
|
+
// Conversion
|
|
526
|
+
"CAST",
|
|
527
|
+
"CONVERT",
|
|
528
|
+
"PARSE",
|
|
529
|
+
"TRY_CAST",
|
|
530
|
+
"TRY_CONVERT",
|
|
531
|
+
"TRY_PARSE",
|
|
532
|
+
// Cryptographic
|
|
533
|
+
"ASYMKEY_ID",
|
|
534
|
+
"ASYMKEYPROPERTY",
|
|
535
|
+
"CERTPROPERTY",
|
|
536
|
+
"CERT_ID",
|
|
537
|
+
"CRYPT_GEN_RANDOM",
|
|
538
|
+
"DECRYPTBYASYMKEY",
|
|
539
|
+
"DECRYPTBYCERT",
|
|
540
|
+
"DECRYPTBYKEY",
|
|
541
|
+
"DECRYPTBYKEYAUTOASYMKEY",
|
|
542
|
+
"DECRYPTBYKEYAUTOCERT",
|
|
543
|
+
"DECRYPTBYPASSPHRASE",
|
|
544
|
+
"ENCRYPTBYASYMKEY",
|
|
545
|
+
"ENCRYPTBYCERT",
|
|
546
|
+
"ENCRYPTBYKEY",
|
|
547
|
+
"ENCRYPTBYPASSPHRASE",
|
|
548
|
+
"HASHBYTES",
|
|
549
|
+
"IS_OBJECTSIGNED",
|
|
550
|
+
"KEY_GUID",
|
|
551
|
+
"KEY_ID",
|
|
552
|
+
"KEY_NAME",
|
|
553
|
+
"SIGNBYASYMKEY",
|
|
554
|
+
"SIGNBYCERT",
|
|
555
|
+
"SYMKEYPROPERTY",
|
|
556
|
+
"VERIFYSIGNEDBYCERT",
|
|
557
|
+
"VERIFYSIGNEDBYASYMKEY",
|
|
558
|
+
// Cursor
|
|
559
|
+
"CURSOR_STATUS",
|
|
560
|
+
// Datatype
|
|
561
|
+
"DATALENGTH",
|
|
562
|
+
"IDENT_CURRENT",
|
|
563
|
+
"IDENT_INCR",
|
|
564
|
+
"IDENT_SEED",
|
|
565
|
+
"IDENTITY",
|
|
566
|
+
"SQL_VARIANT_PROPERTY",
|
|
567
|
+
// Datetime
|
|
568
|
+
"CURRENT_TIMESTAMP",
|
|
569
|
+
"DATEADD",
|
|
570
|
+
"DATEDIFF",
|
|
571
|
+
"DATEFROMPARTS",
|
|
572
|
+
"DATENAME",
|
|
573
|
+
"DATEPART",
|
|
574
|
+
"DATETIME2FROMPARTS",
|
|
575
|
+
"DATETIMEFROMPARTS",
|
|
576
|
+
"DATETIMEOFFSETFROMPARTS",
|
|
577
|
+
"DAY",
|
|
578
|
+
"EOMONTH",
|
|
579
|
+
"GETDATE",
|
|
580
|
+
"GETUTCDATE",
|
|
581
|
+
"ISDATE",
|
|
582
|
+
"MONTH",
|
|
583
|
+
"SMALLDATETIMEFROMPARTS",
|
|
584
|
+
"SWITCHOFFSET",
|
|
585
|
+
"SYSDATETIME",
|
|
586
|
+
"SYSDATETIMEOFFSET",
|
|
587
|
+
"SYSUTCDATETIME",
|
|
588
|
+
"TIMEFROMPARTS",
|
|
589
|
+
"TODATETIMEOFFSET",
|
|
590
|
+
"YEAR",
|
|
591
|
+
// Logical
|
|
592
|
+
"CHOOSE",
|
|
593
|
+
"COALESCE",
|
|
594
|
+
"IIF",
|
|
595
|
+
"NULLIF",
|
|
596
|
+
// Mathematical
|
|
597
|
+
"ABS",
|
|
598
|
+
"ACOS",
|
|
599
|
+
"ASIN",
|
|
600
|
+
"ATAN",
|
|
601
|
+
"ATN2",
|
|
602
|
+
"CEILING",
|
|
603
|
+
"COS",
|
|
604
|
+
"COT",
|
|
605
|
+
"DEGREES",
|
|
606
|
+
"EXP",
|
|
607
|
+
"FLOOR",
|
|
608
|
+
"LOG",
|
|
609
|
+
"LOG10",
|
|
610
|
+
"PI",
|
|
611
|
+
"POWER",
|
|
612
|
+
"RADIANS",
|
|
613
|
+
"RAND",
|
|
614
|
+
"ROUND",
|
|
615
|
+
"SIGN",
|
|
616
|
+
"SIN",
|
|
617
|
+
"SQRT",
|
|
618
|
+
"SQUARE",
|
|
619
|
+
"TAN",
|
|
620
|
+
// Metadata
|
|
621
|
+
"APP_NAME",
|
|
622
|
+
"APPLOCK_MODE",
|
|
623
|
+
"APPLOCK_TEST",
|
|
624
|
+
"ASSEMBLYPROPERTY",
|
|
625
|
+
"COL_LENGTH",
|
|
626
|
+
"COL_NAME",
|
|
627
|
+
"COLUMNPROPERTY",
|
|
628
|
+
"DATABASE_PRINCIPAL_ID",
|
|
629
|
+
"DATABASEPROPERTYEX",
|
|
630
|
+
"DB_ID",
|
|
631
|
+
"DB_NAME",
|
|
632
|
+
"FILE_ID",
|
|
633
|
+
"FILE_IDEX",
|
|
634
|
+
"FILE_NAME",
|
|
635
|
+
"FILEGROUP_ID",
|
|
636
|
+
"FILEGROUP_NAME",
|
|
637
|
+
"FILEGROUPPROPERTY",
|
|
638
|
+
"FILEPROPERTY",
|
|
639
|
+
"FULLTEXTCATALOGPROPERTY",
|
|
640
|
+
"FULLTEXTSERVICEPROPERTY",
|
|
641
|
+
"INDEX_COL",
|
|
642
|
+
"INDEXKEY_PROPERTY",
|
|
643
|
+
"INDEXPROPERTY",
|
|
644
|
+
"OBJECT_DEFINITION",
|
|
645
|
+
"OBJECT_ID",
|
|
646
|
+
"OBJECT_NAME",
|
|
647
|
+
"OBJECT_SCHEMA_NAME",
|
|
648
|
+
"OBJECTPROPERTY",
|
|
649
|
+
"OBJECTPROPERTYEX",
|
|
650
|
+
"ORIGINAL_DB_NAME",
|
|
651
|
+
"PARSENAME",
|
|
652
|
+
"SCHEMA_ID",
|
|
653
|
+
"SCHEMA_NAME",
|
|
654
|
+
"SCOPE_IDENTITY",
|
|
655
|
+
"SERVERPROPERTY",
|
|
656
|
+
"STATS_DATE",
|
|
657
|
+
"TYPE_ID",
|
|
658
|
+
"TYPE_NAME",
|
|
659
|
+
"TYPEPROPERTY",
|
|
660
|
+
// Ranking
|
|
661
|
+
"DENSE_RANK",
|
|
662
|
+
"NTILE",
|
|
663
|
+
"RANK",
|
|
664
|
+
"ROW_NUMBER",
|
|
665
|
+
// Replication
|
|
666
|
+
"PUBLISHINGSERVERNAME",
|
|
667
|
+
// Rowset
|
|
668
|
+
"OPENDATASOURCE",
|
|
669
|
+
"OPENQUERY",
|
|
670
|
+
"OPENROWSET",
|
|
671
|
+
"OPENXML",
|
|
672
|
+
// Security
|
|
673
|
+
"CERTENCODED",
|
|
674
|
+
"CERTPRIVATEKEY",
|
|
675
|
+
"CURRENT_USER",
|
|
676
|
+
"HAS_DBACCESS",
|
|
677
|
+
"HAS_PERMS_BY_NAME",
|
|
678
|
+
"IS_MEMBER",
|
|
679
|
+
"IS_ROLEMEMBER",
|
|
680
|
+
"IS_SRVROLEMEMBER",
|
|
681
|
+
"LOGINPROPERTY",
|
|
682
|
+
"ORIGINAL_LOGIN",
|
|
683
|
+
"PERMISSIONS",
|
|
684
|
+
"PWDENCRYPT",
|
|
685
|
+
"PWDCOMPARE",
|
|
686
|
+
"SESSION_USER",
|
|
687
|
+
"SESSIONPROPERTY",
|
|
688
|
+
"SUSER_ID",
|
|
689
|
+
"SUSER_NAME",
|
|
690
|
+
"SUSER_SID",
|
|
691
|
+
"SUSER_SNAME",
|
|
692
|
+
"SYSTEM_USER",
|
|
693
|
+
"USER",
|
|
694
|
+
"USER_ID",
|
|
695
|
+
"USER_NAME",
|
|
696
|
+
// String
|
|
697
|
+
"ASCII",
|
|
698
|
+
"CHAR",
|
|
699
|
+
"CHARINDEX",
|
|
700
|
+
"CONCAT",
|
|
701
|
+
"DIFFERENCE",
|
|
702
|
+
"FORMAT",
|
|
703
|
+
"LEFT",
|
|
704
|
+
"LEN",
|
|
705
|
+
"LOWER",
|
|
706
|
+
"LTRIM",
|
|
707
|
+
"NCHAR",
|
|
708
|
+
"PATINDEX",
|
|
709
|
+
"QUOTENAME",
|
|
710
|
+
"REPLACE",
|
|
711
|
+
"REPLICATE",
|
|
712
|
+
"REVERSE",
|
|
713
|
+
"RIGHT",
|
|
714
|
+
"RTRIM",
|
|
715
|
+
"SOUNDEX",
|
|
716
|
+
"SPACE",
|
|
717
|
+
"STR",
|
|
718
|
+
"STUFF",
|
|
719
|
+
"SUBSTRING",
|
|
720
|
+
"UNICODE",
|
|
721
|
+
"UPPER",
|
|
722
|
+
// System
|
|
723
|
+
"BINARY_CHECKSUM",
|
|
724
|
+
"CHECKSUM",
|
|
725
|
+
"CONNECTIONPROPERTY",
|
|
726
|
+
"CONTEXT_INFO",
|
|
727
|
+
"CURRENT_REQUEST_ID",
|
|
728
|
+
"ERROR_LINE",
|
|
729
|
+
"ERROR_NUMBER",
|
|
730
|
+
"ERROR_MESSAGE",
|
|
731
|
+
"ERROR_PROCEDURE",
|
|
732
|
+
"ERROR_SEVERITY",
|
|
733
|
+
"ERROR_STATE",
|
|
734
|
+
"FORMATMESSAGE",
|
|
735
|
+
"GETANSINULL",
|
|
736
|
+
"GET_FILESTREAM_TRANSACTION_CONTEXT",
|
|
737
|
+
"HOST_ID",
|
|
738
|
+
"HOST_NAME",
|
|
739
|
+
"ISNULL",
|
|
740
|
+
"ISNUMERIC",
|
|
741
|
+
"MIN_ACTIVE_ROWVERSION",
|
|
742
|
+
"NEWID",
|
|
743
|
+
"NEWSEQUENTIALID",
|
|
744
|
+
"ROWCOUNT_BIG",
|
|
745
|
+
"XACT_STATE",
|
|
746
|
+
// TextImage
|
|
747
|
+
"TEXTPTR",
|
|
748
|
+
"TEXTVALID",
|
|
749
|
+
// Trigger
|
|
750
|
+
"COLUMNS_UPDATED",
|
|
751
|
+
"EVENTDATA",
|
|
752
|
+
"TRIGGER_NESTLEVEL",
|
|
753
|
+
"UPDATE",
|
|
754
|
+
// ChangeTracking
|
|
755
|
+
"CHANGETABLE",
|
|
756
|
+
"CHANGE_TRACKING_CONTEXT",
|
|
757
|
+
"CHANGE_TRACKING_CURRENT_VERSION",
|
|
758
|
+
"CHANGE_TRACKING_IS_COLUMN_IN_MASK",
|
|
759
|
+
"CHANGE_TRACKING_MIN_VALID_VERSION",
|
|
760
|
+
// FullTextSearch
|
|
761
|
+
"CONTAINSTABLE",
|
|
762
|
+
"FREETEXTTABLE",
|
|
763
|
+
// SemanticTextSearch
|
|
764
|
+
"SEMANTICKEYPHRASETABLE",
|
|
765
|
+
"SEMANTICSIMILARITYDETAILSTABLE",
|
|
766
|
+
"SEMANTICSIMILARITYTABLE",
|
|
767
|
+
// FileStream
|
|
768
|
+
"FILETABLEROOTPATH",
|
|
769
|
+
"GETFILENAMESPACEPATH",
|
|
770
|
+
"GETPATHLOCATOR",
|
|
771
|
+
"PATHNAME",
|
|
772
|
+
// ServiceBroker
|
|
773
|
+
"GET_TRANSMISSION_STATUS",
|
|
774
|
+
],
|
|
775
|
+
builtinVariables: [
|
|
776
|
+
// Configuration
|
|
777
|
+
"@@DATEFIRST",
|
|
778
|
+
"@@DBTS",
|
|
779
|
+
"@@LANGID",
|
|
780
|
+
"@@LANGUAGE",
|
|
781
|
+
"@@LOCK_TIMEOUT",
|
|
782
|
+
"@@MAX_CONNECTIONS",
|
|
783
|
+
"@@MAX_PRECISION",
|
|
784
|
+
"@@NESTLEVEL",
|
|
785
|
+
"@@OPTIONS",
|
|
786
|
+
"@@REMSERVER",
|
|
787
|
+
"@@SERVERNAME",
|
|
788
|
+
"@@SERVICENAME",
|
|
789
|
+
"@@SPID",
|
|
790
|
+
"@@TEXTSIZE",
|
|
791
|
+
"@@VERSION",
|
|
792
|
+
// Cursor
|
|
793
|
+
"@@CURSOR_ROWS",
|
|
794
|
+
"@@FETCH_STATUS",
|
|
795
|
+
// Datetime
|
|
796
|
+
"@@DATEFIRST",
|
|
797
|
+
// Metadata
|
|
798
|
+
"@@PROCID",
|
|
799
|
+
// System
|
|
800
|
+
"@@ERROR",
|
|
801
|
+
"@@IDENTITY",
|
|
802
|
+
"@@ROWCOUNT",
|
|
803
|
+
"@@TRANCOUNT",
|
|
804
|
+
// Stats
|
|
805
|
+
"@@CONNECTIONS",
|
|
806
|
+
"@@CPU_BUSY",
|
|
807
|
+
"@@IDLE",
|
|
808
|
+
"@@IO_BUSY",
|
|
809
|
+
"@@PACKET_ERRORS",
|
|
810
|
+
"@@PACK_RECEIVED",
|
|
811
|
+
"@@PACK_SENT",
|
|
812
|
+
"@@TIMETICKS",
|
|
813
|
+
"@@TOTAL_ERRORS",
|
|
814
|
+
"@@TOTAL_READ",
|
|
815
|
+
"@@TOTAL_WRITE",
|
|
816
|
+
],
|
|
817
|
+
pseudoColumns: ["$ACTION", "$IDENTITY", "$ROWGUID", "$PARTITION"],
|
|
818
|
+
tokenizer: {
|
|
819
|
+
root: [
|
|
820
|
+
{ include: "@comments" },
|
|
821
|
+
{ include: "@whitespace" },
|
|
822
|
+
{ include: "@pseudoColumns" },
|
|
823
|
+
{ include: "@numbers" },
|
|
824
|
+
{ include: "@strings" },
|
|
825
|
+
{ include: "@complexIdentifiers" },
|
|
826
|
+
{ include: "@scopes" },
|
|
827
|
+
[/[;,.]/, "delimiter"],
|
|
828
|
+
[/[()]/, "@brackets"],
|
|
829
|
+
[
|
|
830
|
+
/[\w@#$]+/,
|
|
831
|
+
{
|
|
832
|
+
cases: {
|
|
833
|
+
"@operators": "operator",
|
|
834
|
+
"@builtinVariables": "predefined",
|
|
835
|
+
"@builtinFunctions": "predefined",
|
|
836
|
+
"@keywords": "keyword",
|
|
837
|
+
"@default": "identifier",
|
|
838
|
+
},
|
|
839
|
+
},
|
|
840
|
+
],
|
|
841
|
+
[/[<>=!%&+\-*/|~^]/, "operator"],
|
|
842
|
+
],
|
|
843
|
+
whitespace: [[/\s+/, "white"]],
|
|
844
|
+
comments: [
|
|
845
|
+
[/--+.*/, "comment"],
|
|
846
|
+
[/\/\*/, { token: "comment.quote", next: "@comment" }],
|
|
847
|
+
],
|
|
848
|
+
comment: [
|
|
849
|
+
[/[^*/]+/, "comment"],
|
|
850
|
+
// Not supporting nested comments, as nested comments seem to not be standard?
|
|
851
|
+
// i.e. http://stackoverflow.com/questions/728172/are-there-multiline-comment-delimiters-in-sql-that-are-vendor-agnostic
|
|
852
|
+
// [/\/\*/, { token: 'comment.quote', next: '@push' }], // nested comment not allowed :-(
|
|
853
|
+
[/\*\//, { token: "comment.quote", next: "@pop" }],
|
|
854
|
+
[/./, "comment"],
|
|
855
|
+
],
|
|
856
|
+
// TODO: discuss
|
|
857
|
+
pseudoColumns: [
|
|
858
|
+
[
|
|
859
|
+
/[$][A-Za-z_][\w@#$]*/,
|
|
860
|
+
{
|
|
861
|
+
cases: {
|
|
862
|
+
"@pseudoColumns": "predefined",
|
|
863
|
+
"@default": "identifier",
|
|
864
|
+
},
|
|
865
|
+
},
|
|
866
|
+
],
|
|
867
|
+
],
|
|
868
|
+
numbers: [
|
|
869
|
+
[/0[xX][0-9a-fA-F]*/, "number"],
|
|
870
|
+
[/[$][+-]*\d*(\.\d*)?/, "number"],
|
|
871
|
+
[/((\d+(\.\d*)?)|(\.\d+))([eE][\-+]?\d+)?/, "number"],
|
|
872
|
+
],
|
|
873
|
+
strings: [
|
|
874
|
+
[/N'/, { token: "string", next: "@string" }],
|
|
875
|
+
[/'/, { token: "string", next: "@string" }],
|
|
876
|
+
],
|
|
877
|
+
string: [
|
|
878
|
+
[/[^']+/, "string"],
|
|
879
|
+
[/''/, "string"],
|
|
880
|
+
[/'/, { token: "string", next: "@pop" }],
|
|
881
|
+
],
|
|
882
|
+
complexIdentifiers: [
|
|
883
|
+
[/\[/, { token: "identifier.quote", next: "@bracketedIdentifier" }],
|
|
884
|
+
[/"/, { token: "identifier.quote", next: "@quotedIdentifier" }],
|
|
885
|
+
],
|
|
886
|
+
bracketedIdentifier: [
|
|
887
|
+
[/[^\]]+/, "identifier"],
|
|
888
|
+
[/]]/, "identifier"],
|
|
889
|
+
[/]/, { token: "identifier.quote", next: "@pop" }],
|
|
890
|
+
],
|
|
891
|
+
quotedIdentifier: [
|
|
892
|
+
[/[^"]+/, "identifier"],
|
|
893
|
+
[/""/, "identifier"],
|
|
894
|
+
[/"/, { token: "identifier.quote", next: "@pop" }],
|
|
895
|
+
],
|
|
896
|
+
scopes: [
|
|
897
|
+
[/BEGIN\s+(DISTRIBUTED\s+)?TRAN(SACTION)?\b/i, "keyword"],
|
|
898
|
+
[/BEGIN\s+TRY\b/i, { token: "keyword.try" }],
|
|
899
|
+
[/END\s+TRY\b/i, { token: "keyword.try" }],
|
|
900
|
+
[/BEGIN\s+CATCH\b/i, { token: "keyword.catch" }],
|
|
901
|
+
[/END\s+CATCH\b/i, { token: "keyword.catch" }],
|
|
902
|
+
[/(BEGIN|CASE)\b/i, { token: "keyword.block" }],
|
|
903
|
+
[/END\b/i, { token: "keyword.block" }],
|
|
904
|
+
[/WHEN\b/i, { token: "keyword.choice" }],
|
|
905
|
+
[/THEN\b/i, { token: "keyword.choice" }],
|
|
906
|
+
],
|
|
907
|
+
},
|
|
908
|
+
};
|
|
909
|
+
|
|
910
|
+
/* eslint-disable @typescript-eslint/no-magic-numbers */
|
|
911
|
+
async function sqlExpressionCompletion(range, model, position, sdkFunctions) {
|
|
912
|
+
try {
|
|
913
|
+
/*
|
|
914
|
+
Handles the case where we have a valid SQL expression in the editor and is
|
|
915
|
+
able to generate an AST from it and traverse it to provide completion items.
|
|
916
|
+
*/
|
|
917
|
+
const editorValue = model.getValue();
|
|
918
|
+
const sql = await importCoreSql();
|
|
919
|
+
const fieldIndex = await newLayersSupportFieldsIndex([]);
|
|
920
|
+
const { parseTree } = await sql.parseWhereClause(editorValue, fieldIndex);
|
|
921
|
+
const completionItems = getCompletionItems(model, parseTree, range, position, sdkFunctions);
|
|
922
|
+
return completionItems;
|
|
923
|
+
}
|
|
924
|
+
catch {
|
|
925
|
+
/*
|
|
926
|
+
Handles the case where we have an incomplete SQL expression (i.e. if users are
|
|
927
|
+
still typing or has erroneous syntax). We fall back to a more lexical approach
|
|
928
|
+
in this way to "fail gracefully" in order to still provide completion items.
|
|
929
|
+
*/
|
|
930
|
+
const errorRange = {
|
|
931
|
+
startColumn: 1,
|
|
932
|
+
endColumn: position.column,
|
|
933
|
+
startLineNumber: 1,
|
|
934
|
+
endLineNumber: position.lineNumber,
|
|
935
|
+
};
|
|
936
|
+
/* Matching a '(', ')', ' ', or ',' */
|
|
937
|
+
const delimiter = /(?:\(|\)|\s|,)/u;
|
|
938
|
+
/*
|
|
939
|
+
Filter callback function to filter out all non-function words, non-sql keywords, empty
|
|
940
|
+
strings and single quotes ('). Preserve all '(', ')', ',' and 'END' tokens
|
|
941
|
+
within our array.
|
|
942
|
+
*/
|
|
943
|
+
const filterToken = (token) => {
|
|
944
|
+
const tokenIsAFunction = sdkFunctions[token] !== undefined;
|
|
945
|
+
const isParenLessFunction = isSqlParenLessFunction(token);
|
|
946
|
+
const excludedTokens = token !== "" && token !== "'";
|
|
947
|
+
const includedTokens = ["(", ")", ",", "END"].includes(token);
|
|
948
|
+
/*
|
|
949
|
+
Returns the tokens if they are either a standardized function, parenthesis-less function
|
|
950
|
+
or is one of the tokens that we specified to keep. Filters all empty string and
|
|
951
|
+
single quotations.
|
|
952
|
+
*/
|
|
953
|
+
return excludedTokens && (tokenIsAFunction || isParenLessFunction || includedTokens);
|
|
954
|
+
};
|
|
955
|
+
const textValueUpToCursor = model.getValueInRange(errorRange).toUpperCase();
|
|
956
|
+
// TODO: test this...
|
|
957
|
+
const tokens = textValueUpToCursor
|
|
958
|
+
.split(delimiter)
|
|
959
|
+
.map((token) => token.trim())
|
|
960
|
+
.filter((token) => filterToken(token));
|
|
961
|
+
/* Keeps track of our current context item */
|
|
962
|
+
const contextStack = [];
|
|
963
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
964
|
+
const token = tokens[i];
|
|
965
|
+
const nonEmptyContextStack = contextStack.length > 0;
|
|
966
|
+
/*
|
|
967
|
+
As long as we have not hit the last token in our list yet, we can do a
|
|
968
|
+
lookahead and see if the next token makes a valid sub-expression.
|
|
969
|
+
*/
|
|
970
|
+
if (i < tokens.length - 1) {
|
|
971
|
+
const nextToken = tokens[i + 1];
|
|
972
|
+
const invalidFunctionCall = sdkFunctions[token] && nextToken !== "(";
|
|
973
|
+
if (invalidFunctionCall) {
|
|
974
|
+
continue;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
if (token === "(" || token === ",") {
|
|
978
|
+
if (nonEmptyContextStack) {
|
|
979
|
+
const currentContext = contextStack[contextStack.length - 1];
|
|
980
|
+
/*
|
|
981
|
+
This marks the end of our parenthesis-less function's scope, hence
|
|
982
|
+
we cleanse the context and let the next token update the context.
|
|
983
|
+
*/
|
|
984
|
+
if (isSqlParenLessFunction(currentContext)) {
|
|
985
|
+
contextStack.pop();
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
else if (token === ")" || token === "END") {
|
|
991
|
+
/*
|
|
992
|
+
If we have a non empty context stack, that means we are within another
|
|
993
|
+
function's scope right now (another context). We will return to the outer
|
|
994
|
+
scope to provide us the necessary completion items.
|
|
995
|
+
*/
|
|
996
|
+
if (nonEmptyContextStack) {
|
|
997
|
+
contextStack.pop();
|
|
998
|
+
}
|
|
999
|
+
continue;
|
|
1000
|
+
}
|
|
1001
|
+
/*
|
|
1002
|
+
If we end off with a standardized function without a '(', we should not
|
|
1003
|
+
update the context. We should only trigger completion if they have a '('
|
|
1004
|
+
to indicate that it's a function call.
|
|
1005
|
+
*/
|
|
1006
|
+
if (i === tokens.length - 1 && sdkFunctions[token]) {
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
/*
|
|
1010
|
+
Very specific case where we have something like
|
|
1011
|
+
|
|
1012
|
+
| TRIM(INTERVAL INTERVAL, ... |
|
|
1013
|
+
|
|
1014
|
+
and we do not want the completion items for "INTERVAL" to persist even after
|
|
1015
|
+
the context ends.
|
|
1016
|
+
|
|
1017
|
+
In essence, we only update the context once.
|
|
1018
|
+
*/
|
|
1019
|
+
if (i > 0) {
|
|
1020
|
+
const prevTokenIsParenLess = isSqlParenLessFunction(tokens[i - 1]);
|
|
1021
|
+
const curTokenIsParenLess = isSqlParenLessFunction(token);
|
|
1022
|
+
if (!(curTokenIsParenLess && prevTokenIsParenLess)) {
|
|
1023
|
+
contextStack.push(token);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
else if (i === 0) {
|
|
1027
|
+
contextStack.push(token);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
let completionItems = [];
|
|
1031
|
+
[...new Set(contextStack)].forEach((context, index) => {
|
|
1032
|
+
const autoCompletionItem = generateCompletionList(context, range, index);
|
|
1033
|
+
completionItems = [...completionItems, ...autoCompletionItem];
|
|
1034
|
+
});
|
|
1035
|
+
return completionItems;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
/*
|
|
1039
|
+
Helper function that takes in our model/position information and traverse the
|
|
1040
|
+
completed AST to generate an array of Monaco's completion items.
|
|
1041
|
+
|
|
1042
|
+
This function is invoked only if we have a valid SQL expression that can be parsed to
|
|
1043
|
+
generate an AST. In other words, this is only invoked for our "success" case.
|
|
1044
|
+
*/
|
|
1045
|
+
const getCompletionItems = (model, node, range, position, sdkFunctions) => {
|
|
1046
|
+
/*
|
|
1047
|
+
Using an object here so that we can pass by reference to the function to
|
|
1048
|
+
handle updating the state instead of pass by value if we use a primitive
|
|
1049
|
+
type like string. This also allows us to extend the context and add more
|
|
1050
|
+
data and attributes if we need to in the future.
|
|
1051
|
+
*/
|
|
1052
|
+
const context = { contextString: "NONE", additionalContext: "NONE" };
|
|
1053
|
+
traverseAST({ model, node, position, context, sdkFunctions });
|
|
1054
|
+
const { contextString, additionalContext } = context;
|
|
1055
|
+
const completionItems = generateCompletionList(contextString, range, 0);
|
|
1056
|
+
const additionalItems = generateCompletionList(additionalContext, range, 1);
|
|
1057
|
+
return [...completionItems, ...additionalItems];
|
|
1058
|
+
};
|
|
1059
|
+
/*
|
|
1060
|
+
This function traverses the AST and finds which node and context we are currently
|
|
1061
|
+
in to help provide the necessary completion items. This follows from having a complete
|
|
1062
|
+
and valid SQL expression. The main purpose of this function is to provide the correct
|
|
1063
|
+
completion items when users trigger IntelliSense via CTRL + Space.
|
|
1064
|
+
|
|
1065
|
+
This is a recursive function.
|
|
1066
|
+
*/
|
|
1067
|
+
// eslint-disable-next-line complexity
|
|
1068
|
+
const traverseAST = (
|
|
1069
|
+
/*
|
|
1070
|
+
Keeping the node type as generic 'any' here as __esri.SQLNode does not have updated types
|
|
1071
|
+
with the addition of the range property. Documentation is outdated and may or may not
|
|
1072
|
+
contain mismatching property names (ListNode uses "expr" as property for all expressions
|
|
1073
|
+
but representation is via "value") and some missing cases (e.g. case "parameter" is not
|
|
1074
|
+
identified).
|
|
1075
|
+
*/
|
|
1076
|
+
{ model, node, position, context, sdkFunctions, delimiterPos, }) => {
|
|
1077
|
+
/* Our current cursor position within the editor */
|
|
1078
|
+
// const cursorRow: number = position.lineNumber;
|
|
1079
|
+
// const cursorColumn: number = position.column;
|
|
1080
|
+
switch (node.type) {
|
|
1081
|
+
case "interval": {
|
|
1082
|
+
/*
|
|
1083
|
+
Here, we are using an interval node as a standalone and not within any
|
|
1084
|
+
function call or expression list.
|
|
1085
|
+
*/
|
|
1086
|
+
if (!delimiterPos) {
|
|
1087
|
+
updateContext(context, "INTERVAL", false);
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
/*
|
|
1091
|
+
We can ensure that the match is never null since a fully generated AST
|
|
1092
|
+
follows from a syntactically correct SQL expression.
|
|
1093
|
+
*/
|
|
1094
|
+
const nextDelimPos = model.findNextMatch(
|
|
1095
|
+
// @ts-ignore
|
|
1096
|
+
/\)|,/u, {
|
|
1097
|
+
lineNumber: node.location.end.line,
|
|
1098
|
+
column: node.location.end.column,
|
|
1099
|
+
}, true, true, null, true).range;
|
|
1100
|
+
const [inlineFunc, funcWrapped] = getCursorWithinRange(position, delimiterPos, nextDelimPos);
|
|
1101
|
+
if (inlineFunc ?? funcWrapped) {
|
|
1102
|
+
updateContext(context, "INTERVAL", false);
|
|
1103
|
+
}
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
case "case-expression": {
|
|
1107
|
+
/* Only update context if we are within the range */
|
|
1108
|
+
// Representing the range for the "CASE" keyword for starting the case expression
|
|
1109
|
+
const startPos = {
|
|
1110
|
+
startColumn: node.location.start.column,
|
|
1111
|
+
endColumn: node.location.start.column + 4,
|
|
1112
|
+
startLineNumber: node.location.start.line,
|
|
1113
|
+
endLineNumber: node.location.start.line,
|
|
1114
|
+
};
|
|
1115
|
+
// Representing the range for the "END" keyword for ending the case expression
|
|
1116
|
+
const endPos = {
|
|
1117
|
+
startColumn: node.location.end.column - 3,
|
|
1118
|
+
endColumn: node.location.end.column,
|
|
1119
|
+
startLineNumber: node.location.end.line,
|
|
1120
|
+
endLineNumber: node.location.end.line,
|
|
1121
|
+
};
|
|
1122
|
+
const [inline, wrapped] = getCursorWithinRange(position, startPos, endPos);
|
|
1123
|
+
if (inline ?? wrapped) {
|
|
1124
|
+
updateContext(context, "CASE", false);
|
|
1125
|
+
}
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
case "expression-list": {
|
|
1129
|
+
const exprList = node.value;
|
|
1130
|
+
let delimiter;
|
|
1131
|
+
exprList.forEach((expr, index) => {
|
|
1132
|
+
/* The position of our opening delimiter '(' */
|
|
1133
|
+
if (index === 0) {
|
|
1134
|
+
const match = model.findMatches("(", {
|
|
1135
|
+
startLineNumber: 1,
|
|
1136
|
+
endLineNumber: node.location.start.line,
|
|
1137
|
+
startColumn: 1,
|
|
1138
|
+
endColumn: node.location.start.column,
|
|
1139
|
+
}, false, true, null, true);
|
|
1140
|
+
delimiter = match[match.length - 1].range;
|
|
1141
|
+
}
|
|
1142
|
+
traverseAST({ model, node: expr, position, context, sdkFunctions, delimiterPos: delimiter });
|
|
1143
|
+
/*
|
|
1144
|
+
Finds and updates our next delimiter position (i.e. the delimiter that immediately
|
|
1145
|
+
follows this current node)
|
|
1146
|
+
*/
|
|
1147
|
+
delimiter = getDelimiterPosition(index + 1, expr, model, exprList.length);
|
|
1148
|
+
});
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
case "unary-expression":
|
|
1152
|
+
traverseAST({ model, node: node.expr, position, context, sdkFunctions });
|
|
1153
|
+
return;
|
|
1154
|
+
case "binary-expression":
|
|
1155
|
+
traverseAST({ model, node: node.left, position, context, sdkFunctions });
|
|
1156
|
+
traverseAST({ model, node: node.right, position, context, sdkFunctions });
|
|
1157
|
+
return;
|
|
1158
|
+
case "null":
|
|
1159
|
+
case "boolean":
|
|
1160
|
+
case "string":
|
|
1161
|
+
case "timestamp":
|
|
1162
|
+
case "date":
|
|
1163
|
+
case "time":
|
|
1164
|
+
case "number":
|
|
1165
|
+
case "current-time":
|
|
1166
|
+
return;
|
|
1167
|
+
case "column-reference": {
|
|
1168
|
+
const columnName = node.column;
|
|
1169
|
+
const isParenLessFunction = isSqlParenLessFunction(columnName);
|
|
1170
|
+
/*
|
|
1171
|
+
In this case, we are using a standalone column reference, not a part of any
|
|
1172
|
+
expression list or function param. Update the context accordingly if it is
|
|
1173
|
+
any of the parenthesis-less functions.
|
|
1174
|
+
*/
|
|
1175
|
+
if (!delimiterPos) {
|
|
1176
|
+
if (isParenLessFunction) {
|
|
1177
|
+
updateContext(context, columnName, false);
|
|
1178
|
+
}
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
/*
|
|
1182
|
+
The delimiter right before we start our expression. This delimiter is useful for us
|
|
1183
|
+
as it gives positional information for when the expression starts if it's used as a
|
|
1184
|
+
parameter in a function.
|
|
1185
|
+
|
|
1186
|
+
For example, if we have something like Function(a, b), then the range of b would have
|
|
1187
|
+
a start column value of 13 (offset of 12) whereas "," would be 11 (offset of 10).
|
|
1188
|
+
|
|
1189
|
+
This will be useful in the case where we want to provide completion items before our
|
|
1190
|
+
keyword appears (if any).
|
|
1191
|
+
*/
|
|
1192
|
+
const delimiterCharacter = model.getValueInRange(delimiterPos);
|
|
1193
|
+
const nextDelimiterPos = model.findNextMatch(
|
|
1194
|
+
/*
|
|
1195
|
+
Function call only accepts string even though docs noted that RegExp is allowed.
|
|
1196
|
+
"@param searchString — The string used to search. If it is a regular expression,
|
|
1197
|
+
set isRegex to true"
|
|
1198
|
+
*/
|
|
1199
|
+
// @ts-ignore
|
|
1200
|
+
/\)|,/u, {
|
|
1201
|
+
column: node.location.start.column,
|
|
1202
|
+
lineNumber: node.location.start.line,
|
|
1203
|
+
}, true, true, null, true).range;
|
|
1204
|
+
const [inlineWithFunction, wrappedWithinFunction] = getCursorWithinRange(position, delimiterPos, nextDelimiterPos);
|
|
1205
|
+
if (inlineWithFunction ?? wrappedWithinFunction) {
|
|
1206
|
+
/*
|
|
1207
|
+
We updated our additional context here if this is our first argument within a function call.
|
|
1208
|
+
This is so we don't override the previous context with our current context, which may
|
|
1209
|
+
strip away certain completion items. For example, if we have the SQL expression
|
|
1210
|
+
CAST(INTERVAL), we still want the completion items for CAST as well as INTERVAL.
|
|
1211
|
+
*/
|
|
1212
|
+
if (isParenLessFunction) {
|
|
1213
|
+
updateContext(context, columnName, delimiterCharacter === "(");
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
case "data-type":
|
|
1219
|
+
return;
|
|
1220
|
+
case "function": {
|
|
1221
|
+
const functionName = node.name;
|
|
1222
|
+
const argsList = node.args.value;
|
|
1223
|
+
const startRange = {
|
|
1224
|
+
startLineNumber: node.location.start.line,
|
|
1225
|
+
endLineNumber: node.location.start.line,
|
|
1226
|
+
startColumn: node.location.start.column,
|
|
1227
|
+
endColumn: node.location.start.column + functionName.length,
|
|
1228
|
+
};
|
|
1229
|
+
const endRange = {
|
|
1230
|
+
startLineNumber: node.location.end.line,
|
|
1231
|
+
endLineNumber: node.location.end.line,
|
|
1232
|
+
startColumn: node.location.end.column,
|
|
1233
|
+
endColumn: node.location.end.column,
|
|
1234
|
+
};
|
|
1235
|
+
const [inlineWithFunc, wrappedWithinFunc] = getCursorWithinRange(position, startRange, endRange);
|
|
1236
|
+
if (inlineWithFunc ?? wrappedWithinFunc) {
|
|
1237
|
+
updateContext(context, functionName, false);
|
|
1238
|
+
}
|
|
1239
|
+
let delimiterPosition;
|
|
1240
|
+
argsList.forEach((arg, index) => {
|
|
1241
|
+
const nodeType = arg.type;
|
|
1242
|
+
let completionNodeType = false;
|
|
1243
|
+
switch (nodeType) {
|
|
1244
|
+
case "function":
|
|
1245
|
+
case "column-reference":
|
|
1246
|
+
case "interval":
|
|
1247
|
+
case "case-expression":
|
|
1248
|
+
completionNodeType = true;
|
|
1249
|
+
break;
|
|
1250
|
+
}
|
|
1251
|
+
/*
|
|
1252
|
+
We can ensure that the match is never undefined since a fully generated AST
|
|
1253
|
+
follows from a syntactically correct SQL expression.
|
|
1254
|
+
*/
|
|
1255
|
+
if (index === 0) {
|
|
1256
|
+
delimiterPosition = model.findNextMatch("(", {
|
|
1257
|
+
column: node.location.start.column,
|
|
1258
|
+
lineNumber: node.location.start.line,
|
|
1259
|
+
}, false, true, null, true).range;
|
|
1260
|
+
}
|
|
1261
|
+
/* TRIM(abc) === TRIM(BOTH ' ' FROM abc). In this case, our next delimiter is the end */
|
|
1262
|
+
const trimFuncDefault = arg.type === "string" && arg.value === "BOTH";
|
|
1263
|
+
/*
|
|
1264
|
+
If the current node is not a completion node type, and is the default trim function,
|
|
1265
|
+
then we will just use the current node as a reference to get the next delimiter position.
|
|
1266
|
+
*/
|
|
1267
|
+
if (!completionNodeType && trimFuncDefault) {
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
traverseAST({ model, node: arg, position, context, sdkFunctions, delimiterPos: delimiterPosition });
|
|
1271
|
+
delimiterPosition = getDelimiterPosition(index + 1, arg, model, argsList.length);
|
|
1272
|
+
});
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
/* Currently no support for parameter type (@) */
|
|
1276
|
+
case "parameter":
|
|
1277
|
+
case "when-clause":
|
|
1278
|
+
case "interval-period":
|
|
1279
|
+
case "interval-qualifier":
|
|
1280
|
+
return;
|
|
1281
|
+
default:
|
|
1282
|
+
throw new Error("Invalid syntax.");
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
/*
|
|
1286
|
+
Helper function that generates our list of completion items after traversing
|
|
1287
|
+
our AST and updating the context.
|
|
1288
|
+
|
|
1289
|
+
The sorting number is used as a way of grouping and organizing our completion lists.
|
|
1290
|
+
|
|
1291
|
+
- All default completion items have a sort value of 'z' (ASCII 122) + their corresponding
|
|
1292
|
+
grouping value (e.g. 'za', 'zb', etc...)
|
|
1293
|
+
- All completion items found by cursor matching have a value between 'a' - 'y' (ASCII 97-121)
|
|
1294
|
+
- The higher the sorting number, the higher it will appear on our completion list
|
|
1295
|
+
*/
|
|
1296
|
+
const generateCompletionList = (functionName, range, sortingNumber) => {
|
|
1297
|
+
const identifiers = getKeywords(functionName);
|
|
1298
|
+
const completionItems = identifiers?.map((identifier) => {
|
|
1299
|
+
const dataKind = dataTypes.includes(identifier.toUpperCase())
|
|
1300
|
+
? languages.CompletionItemKind.TypeParameter
|
|
1301
|
+
: languages.CompletionItemKind.Keyword;
|
|
1302
|
+
return {
|
|
1303
|
+
label: identifier,
|
|
1304
|
+
kind: dataKind,
|
|
1305
|
+
insertText: identifier,
|
|
1306
|
+
range,
|
|
1307
|
+
sortText: String.fromCharCode(121 - sortingNumber),
|
|
1308
|
+
};
|
|
1309
|
+
});
|
|
1310
|
+
return completionItems ?? [];
|
|
1311
|
+
};
|
|
1312
|
+
/*
|
|
1313
|
+
Function to help us update our current context depending on where our cursor
|
|
1314
|
+
is within the editor. This will allow us to gather the relevant completion items
|
|
1315
|
+
depending on the value of the context (i.e. the function that our cursor is within).
|
|
1316
|
+
|
|
1317
|
+
This is invoked only during our traversal of the AST (i.e. "success" case).
|
|
1318
|
+
*/
|
|
1319
|
+
const updateContext = (oldContext, newContext, updateAdditionalContext) => {
|
|
1320
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
1321
|
+
updateAdditionalContext ? (oldContext.additionalContext = newContext) : (oldContext.contextString = newContext);
|
|
1322
|
+
};
|
|
1323
|
+
/** source */
|
|
1324
|
+
const sqlBinOps = [
|
|
1325
|
+
"AND",
|
|
1326
|
+
"OR",
|
|
1327
|
+
"IS",
|
|
1328
|
+
"ISNOT",
|
|
1329
|
+
"IN",
|
|
1330
|
+
"NOT IN",
|
|
1331
|
+
"BETWEEN",
|
|
1332
|
+
"NOTBETWEEN",
|
|
1333
|
+
"LIKE",
|
|
1334
|
+
"NOT LIKE",
|
|
1335
|
+
];
|
|
1336
|
+
/*
|
|
1337
|
+
The current state of sorting completion items.
|
|
1338
|
+
|
|
1339
|
+
All fields that are provided by default (keywords, standardized functions, constants, etc.)
|
|
1340
|
+
all have a sort value of "z". As we parse our SQL Expression and traverse an AST (if we can),
|
|
1341
|
+
we will have new completion items depending on our cursor context. These completion items
|
|
1342
|
+
should appear first at the top of our list, with the most recent context having the highest
|
|
1343
|
+
appearance on our list. In this manner, we add new completion items with a smaller
|
|
1344
|
+
lexicographic value than the previous to promote the correct ordering. (I.e. add in new completion
|
|
1345
|
+
items with a sort value of "y" next, then "x", then "w", all the way until "a". It is unlikely
|
|
1346
|
+
that we will need more than 26 difference context but if we do require more, we can make use of
|
|
1347
|
+
double-lettered sort values).
|
|
1348
|
+
*/
|
|
1349
|
+
const sortTextValues = {
|
|
1350
|
+
identifiers: "a",
|
|
1351
|
+
fields: "aa",
|
|
1352
|
+
constants: "ab",
|
|
1353
|
+
literals: "ab",
|
|
1354
|
+
functions: "ac",
|
|
1355
|
+
snippets: "b",
|
|
1356
|
+
keywords: "c",
|
|
1357
|
+
dataType: "d",
|
|
1358
|
+
};
|
|
1359
|
+
function generateCompletionItems(fields, range, sdkFunctions) {
|
|
1360
|
+
const fieldsCompletion = fields.map((field) => ({
|
|
1361
|
+
label: field.completion?.label ?? field.name,
|
|
1362
|
+
insertText: field.completion?.insertText ?? field.name,
|
|
1363
|
+
sortText: `z${sortTextValues.fields}`,
|
|
1364
|
+
detail: field.completion?.detail,
|
|
1365
|
+
range,
|
|
1366
|
+
kind: languages.CompletionItemKind.Variable,
|
|
1367
|
+
documentation: field.completion?.documentation,
|
|
1368
|
+
}));
|
|
1369
|
+
// {
|
|
1370
|
+
// const funcName = func.toLowerCase();
|
|
1371
|
+
// const apiContext = apiFunctions[funcName as keyof typeof apiFunctions] as ApiContextType;
|
|
1372
|
+
// const { name, snippet, parameters, returnValue, description, examples } = apiContext;
|
|
1373
|
+
// const completionItemData = {
|
|
1374
|
+
// kind: monaco.languages.CompletionItemKind.Function,
|
|
1375
|
+
// range,
|
|
1376
|
+
// sortText: `z${sortTextValues.functions}`,
|
|
1377
|
+
// detail: generateTypeDescription(name, parameters, returnValue),
|
|
1378
|
+
// documentation: generateCompletionDocumentation(description, examples),
|
|
1379
|
+
// };
|
|
1380
|
+
// /*
|
|
1381
|
+
// We take 2 different forms of the same function over our REST API.
|
|
1382
|
+
// e.g. CURRENT_TIME and CURRENT_TIME() are both accepted.
|
|
1383
|
+
// */
|
|
1384
|
+
// if (Array.isArray(snippet) && snippet.length) {
|
|
1385
|
+
// return [
|
|
1386
|
+
// { ...completionItemData, label: name, insertText: snippet[0]! },
|
|
1387
|
+
// snippet[1] && { ...completionItemData, label: name.slice(0, -2), insertText: snippet[1] },
|
|
1388
|
+
// ];
|
|
1389
|
+
// }
|
|
1390
|
+
// return [{ ...completionItemData, label: name, insertText: snippet }];
|
|
1391
|
+
// }
|
|
1392
|
+
// );
|
|
1393
|
+
const sqlBinOpsCompletion = sqlBinOps.map((binOp) => {
|
|
1394
|
+
const formatOperator = (binOp) => {
|
|
1395
|
+
if (binOp === "ISNOT") {
|
|
1396
|
+
return "IS NOT";
|
|
1397
|
+
}
|
|
1398
|
+
if (binOp === "NOTBETWEEN") {
|
|
1399
|
+
return "NOT BETWEEN";
|
|
1400
|
+
}
|
|
1401
|
+
return binOp;
|
|
1402
|
+
};
|
|
1403
|
+
return {
|
|
1404
|
+
label: formatOperator(binOp),
|
|
1405
|
+
kind: languages.CompletionItemKind.Keyword,
|
|
1406
|
+
insertText: formatOperator(binOp),
|
|
1407
|
+
range,
|
|
1408
|
+
sortText: `z${sortTextValues.keywords}`,
|
|
1409
|
+
};
|
|
1410
|
+
});
|
|
1411
|
+
const sqlKeywordsCompletion = sqlParenLessFunctions.map((keyword) => {
|
|
1412
|
+
if (keyword === "CASE") {
|
|
1413
|
+
return {
|
|
1414
|
+
label: keyword,
|
|
1415
|
+
kind: languages.CompletionItemKind.Snippet,
|
|
1416
|
+
insertText: `CASE
|
|
1417
|
+
WHEN condition1 THEN result1
|
|
1418
|
+
ELSE result
|
|
1419
|
+
END`,
|
|
1420
|
+
range,
|
|
1421
|
+
sortText: `z${sortTextValues.snippets}`,
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
return {
|
|
1425
|
+
label: keyword,
|
|
1426
|
+
kind: languages.CompletionItemKind.Keyword,
|
|
1427
|
+
insertText: keyword,
|
|
1428
|
+
range,
|
|
1429
|
+
sortText: `z${sortTextValues.keywords}`,
|
|
1430
|
+
};
|
|
1431
|
+
});
|
|
1432
|
+
const sqlDataTypeCompletion = dataTypes.map((dataType) => ({
|
|
1433
|
+
label: dataType,
|
|
1434
|
+
kind: languages.CompletionItemKind.TypeParameter,
|
|
1435
|
+
insertText: dataType,
|
|
1436
|
+
range,
|
|
1437
|
+
sortText: `z${sortTextValues.dataType}`,
|
|
1438
|
+
}));
|
|
1439
|
+
const sqlFunctionCompletion = Object.values(sdkFunctions).map((apiItem) => {
|
|
1440
|
+
const entry = apiItem.completion;
|
|
1441
|
+
const item = {
|
|
1442
|
+
label: entry.label,
|
|
1443
|
+
insertText: entry.insertText || entry.label,
|
|
1444
|
+
sortText: `z${sortTextValues.functions}`,
|
|
1445
|
+
filterText: entry.filterText,
|
|
1446
|
+
detail: entry.detail,
|
|
1447
|
+
range,
|
|
1448
|
+
kind: toCompletionItemKind(entry.kind),
|
|
1449
|
+
};
|
|
1450
|
+
if (entry.insertTextFormat === main.exports.InsertTextFormat.Snippet) {
|
|
1451
|
+
item.insertTextRules = languages.CompletionItemInsertTextRule.InsertAsSnippet;
|
|
1452
|
+
}
|
|
1453
|
+
if (entry.documentation) {
|
|
1454
|
+
if (typeof entry.documentation === "string") {
|
|
1455
|
+
item.documentation = entry.documentation;
|
|
1456
|
+
}
|
|
1457
|
+
else {
|
|
1458
|
+
item.documentation = {
|
|
1459
|
+
supportThemeIcons: false,
|
|
1460
|
+
value: entry.documentation.value,
|
|
1461
|
+
supportHtml: true,
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
return item;
|
|
1466
|
+
});
|
|
1467
|
+
return {
|
|
1468
|
+
fieldsCompletion,
|
|
1469
|
+
sqlFunctionCompletion,
|
|
1470
|
+
sqlBinOpsCompletion,
|
|
1471
|
+
sqlKeywordsCompletion,
|
|
1472
|
+
sqlDataTypeCompletion,
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// list of properties that sit on a SQLNode and can have children
|
|
1477
|
+
var ChildBearingProperties;
|
|
1478
|
+
(function (ChildBearingProperties) {
|
|
1479
|
+
ChildBearingProperties["ARGS"] = "args";
|
|
1480
|
+
ChildBearingProperties["CLAUSES"] = "clauses";
|
|
1481
|
+
ChildBearingProperties["ELSE"] = "else";
|
|
1482
|
+
ChildBearingProperties["END"] = "end";
|
|
1483
|
+
ChildBearingProperties["EXPR"] = "expr";
|
|
1484
|
+
ChildBearingProperties["LEFT"] = "left";
|
|
1485
|
+
ChildBearingProperties["OPERAND"] = "operand";
|
|
1486
|
+
ChildBearingProperties["QUALIFIER"] = "qualifier";
|
|
1487
|
+
ChildBearingProperties["RIGHT"] = "right";
|
|
1488
|
+
ChildBearingProperties["START"] = "start";
|
|
1489
|
+
ChildBearingProperties["VALUE"] = "value";
|
|
1490
|
+
})(ChildBearingProperties || (ChildBearingProperties = {}));
|
|
1491
|
+
// list of node types that can have children
|
|
1492
|
+
var ChildBearingNodeTypes;
|
|
1493
|
+
(function (ChildBearingNodeTypes) {
|
|
1494
|
+
ChildBearingNodeTypes["CASE_EXPRESSION"] = "case-expression";
|
|
1495
|
+
ChildBearingNodeTypes["EXPRESSION_LIST"] = "expression-list";
|
|
1496
|
+
ChildBearingNodeTypes["FUNCTION"] = "function";
|
|
1497
|
+
ChildBearingNodeTypes["INTERVAL"] = "interval";
|
|
1498
|
+
ChildBearingNodeTypes["INTERVAL_QUALIFIER"] = "interval-qualifier";
|
|
1499
|
+
ChildBearingNodeTypes["WHEN_CLAUSE"] = "when-clause";
|
|
1500
|
+
ChildBearingNodeTypes["UNARY_EXPRESSION"] = "unary-expression";
|
|
1501
|
+
ChildBearingNodeTypes["BINARY_EXPRESSION"] = "binary-expression";
|
|
1502
|
+
})(ChildBearingNodeTypes || (ChildBearingNodeTypes = {}));
|
|
1503
|
+
const sqlFunctionParamKeywords = {
|
|
1504
|
+
CAST: ["DATE", "FLOAT", "INTEGER", "REAL", "SMALLINT", "TIME", "TIMESTAMP", "VARCHAR"],
|
|
1505
|
+
EXTRACT: ["YEAR", "MONTH", "DAY", "HOUR", "MINUTE", "SECOND"],
|
|
1506
|
+
TRIM: ["LEADING", "TRAILING", "BOTH"],
|
|
1507
|
+
};
|
|
1508
|
+
|
|
1509
|
+
/* eslint-disable @typescript-eslint/no-magic-numbers */
|
|
1510
|
+
function transposeSdkFunctions(sdkLibrary) {
|
|
1511
|
+
return sdkLibrary.flatMap((category) => category.items).reduce((acc, item) => ({ ...acc, [item.name]: item }), {});
|
|
1512
|
+
}
|
|
1513
|
+
class SqlExprCompletionProvider {
|
|
1514
|
+
constructor(
|
|
1515
|
+
// private _worker: ISqlExprWorkerAccessor,
|
|
1516
|
+
_defaults) {
|
|
1517
|
+
this._defaults = _defaults;
|
|
1518
|
+
this._sdkFunctions = transposeSdkFunctions([]);
|
|
1519
|
+
}
|
|
1520
|
+
async provideCompletionItems(model, position) {
|
|
1521
|
+
const word = model.getWordUntilPosition(position);
|
|
1522
|
+
const range = {
|
|
1523
|
+
startLineNumber: position.lineNumber,
|
|
1524
|
+
endLineNumber: position.lineNumber,
|
|
1525
|
+
startColumn: word.startColumn,
|
|
1526
|
+
endColumn: word.endColumn,
|
|
1527
|
+
};
|
|
1528
|
+
const apiContext = this._defaults.getApiContextForModel(model.uri);
|
|
1529
|
+
// get the fields from the api context
|
|
1530
|
+
const fields = apiContext.profile ? getFieldsFromLayerVariable(apiContext.profile) : [];
|
|
1531
|
+
const sdkLibrary = await this._defaults.getApiLibrary(model.uri.toString());
|
|
1532
|
+
this._sdkFunctions = transposeSdkFunctions(sdkLibrary);
|
|
1533
|
+
const sqlContextCompletion = await sqlExpressionCompletion(range, model, position, this._sdkFunctions);
|
|
1534
|
+
const { sqlKeywordsCompletion, sqlBinOpsCompletion, sqlFunctionCompletion, fieldsCompletion, sqlDataTypeCompletion, } = generateCompletionItems(fields, range, this._sdkFunctions);
|
|
1535
|
+
/*
|
|
1536
|
+
Filters our completion items list so that if we happen to have the same label but
|
|
1537
|
+
different sortText order, we keep the sortText with the lowest ASCII value (i.e.
|
|
1538
|
+
the smallest character in terms of lexicographic ordering).
|
|
1539
|
+
*/
|
|
1540
|
+
const filteredItems = new Map();
|
|
1541
|
+
[
|
|
1542
|
+
...sqlKeywordsCompletion,
|
|
1543
|
+
...sqlBinOpsCompletion,
|
|
1544
|
+
...sqlDataTypeCompletion,
|
|
1545
|
+
...sqlFunctionCompletion,
|
|
1546
|
+
...fieldsCompletion,
|
|
1547
|
+
...sqlContextCompletion,
|
|
1548
|
+
].forEach((item) => {
|
|
1549
|
+
const { sortText } = item;
|
|
1550
|
+
const label = item.label;
|
|
1551
|
+
const itemExists = filteredItems.get(label);
|
|
1552
|
+
if (!itemExists) {
|
|
1553
|
+
filteredItems.set(label, item);
|
|
1554
|
+
}
|
|
1555
|
+
const currentOrder = sortText.charCodeAt(0);
|
|
1556
|
+
const currentLowestOrder = filteredItems.get(label).sortText.charCodeAt(0);
|
|
1557
|
+
if (currentOrder < currentLowestOrder) {
|
|
1558
|
+
filteredItems.set(label, item);
|
|
1559
|
+
}
|
|
1560
|
+
});
|
|
1561
|
+
const completionItems = Array.from(filteredItems.entries()).map(([, value]) => value);
|
|
1562
|
+
return {
|
|
1563
|
+
suggestions: completionItems,
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
// TODO: create DiagnosticsAdapter common class
|
|
1568
|
+
class SqlExprDiagnosticsAdapter {
|
|
1569
|
+
constructor(_languageId, _worker, { defaults, diagnosticService, }) {
|
|
1570
|
+
this._languageId = _languageId;
|
|
1571
|
+
this._worker = _worker;
|
|
1572
|
+
this._disposables = [];
|
|
1573
|
+
this._listener = new Map();
|
|
1574
|
+
this._diagnosticsService = diagnosticService;
|
|
1575
|
+
this._defaults = defaults;
|
|
1576
|
+
const onModelAdd = (model) => {
|
|
1577
|
+
const languageId = model.getLanguageId();
|
|
1578
|
+
if (languageId !== this._languageId) {
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
const debouncedValidate = debounce(() => {
|
|
1582
|
+
this._doValidate(model, languageId).catch((err) => {
|
|
1583
|
+
throw err;
|
|
1584
|
+
});
|
|
1585
|
+
});
|
|
1586
|
+
this._listener.set(model.uri.toString(), model.onDidChangeContent(debouncedValidate));
|
|
1587
|
+
this._doValidate(model, languageId).catch(console.error);
|
|
1588
|
+
};
|
|
1589
|
+
const onModelRemoved = (model) => {
|
|
1590
|
+
const uri = model.uri.toString();
|
|
1591
|
+
editor.setModelMarkers(model, this._languageId, []);
|
|
1592
|
+
const listener = this._listener.get(uri);
|
|
1593
|
+
if (listener) {
|
|
1594
|
+
listener.dispose();
|
|
1595
|
+
this._listener.delete(uri);
|
|
1596
|
+
}
|
|
1597
|
+
};
|
|
1598
|
+
this._disposables.push(editor.onDidCreateModel(onModelAdd));
|
|
1599
|
+
this._disposables.push(editor.onWillDisposeModel((model) => onModelRemoved(model)));
|
|
1600
|
+
this._disposables.push(editor.onDidChangeModelLanguage((event) => {
|
|
1601
|
+
onModelRemoved(event.model);
|
|
1602
|
+
onModelAdd(event.model);
|
|
1603
|
+
}));
|
|
1604
|
+
this._disposables.push(defaults.onDidChange(() => {
|
|
1605
|
+
editor.getModels().forEach((model) => {
|
|
1606
|
+
if (model.getLanguageId() === this._languageId) {
|
|
1607
|
+
onModelRemoved(model);
|
|
1608
|
+
onModelAdd(model);
|
|
1609
|
+
}
|
|
1610
|
+
});
|
|
1611
|
+
}));
|
|
1612
|
+
this._disposables.push(defaults.onModelContextDidChange((key) => {
|
|
1613
|
+
editor.getModels().forEach((model) => {
|
|
1614
|
+
if (model.getLanguageId() === this._languageId && model.uri.toString() === key) {
|
|
1615
|
+
this._doValidate(model, this._languageId).catch(console.error);
|
|
1616
|
+
}
|
|
1617
|
+
});
|
|
1618
|
+
}));
|
|
1619
|
+
this._disposables.push({
|
|
1620
|
+
dispose: () => {
|
|
1621
|
+
this._listener.forEach((value) => value.dispose());
|
|
1622
|
+
this._listener.clear();
|
|
1623
|
+
},
|
|
1624
|
+
});
|
|
1625
|
+
editor.getModels().forEach(onModelAdd);
|
|
1626
|
+
}
|
|
1627
|
+
async _doValidate(model, languageId) {
|
|
1628
|
+
if (!model.isAttachedToEditor()) {
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1631
|
+
try {
|
|
1632
|
+
const workerProxy = await this._worker(model.uri);
|
|
1633
|
+
const context = this._defaults.getApiContextForModel(model.uri);
|
|
1634
|
+
const diagnostics = await workerProxy.doValidation(model.uri.toString(), context);
|
|
1635
|
+
this._diagnosticsService.fireDiagnosticsChange(model.uri, diagnostics);
|
|
1636
|
+
editor.setModelMarkers(model, languageId, diagnostics);
|
|
1637
|
+
}
|
|
1638
|
+
catch (err) {
|
|
1639
|
+
console.error(err);
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
const sqlParenLessFunctions = ["INTERVAL", "CASE"];
|
|
1644
|
+
const dataTypes = sqlFunctionParamKeywords.CAST;
|
|
1645
|
+
/*
|
|
1646
|
+
Helper function that takes in a context string and returns a string list of
|
|
1647
|
+
all the keywords that are used by that identifier. This is used to generate
|
|
1648
|
+
our completion items list for specific contexts.
|
|
1649
|
+
*/
|
|
1650
|
+
const getKeywords = (identifier) => {
|
|
1651
|
+
switch (identifier.toLowerCase()) {
|
|
1652
|
+
case "extract":
|
|
1653
|
+
/* Need to revisit TIMEZONE_HOUR and TIMEZONE_MINUTE with the team */
|
|
1654
|
+
return sqlFunctionParamKeywords.EXTRACT;
|
|
1655
|
+
case "position":
|
|
1656
|
+
return ["IN"];
|
|
1657
|
+
case "trim":
|
|
1658
|
+
return sqlFunctionParamKeywords.TRIM;
|
|
1659
|
+
case "cast":
|
|
1660
|
+
return ["AS", ...sqlFunctionParamKeywords.CAST];
|
|
1661
|
+
case "interval":
|
|
1662
|
+
return [...sqlFunctionParamKeywords.EXTRACT];
|
|
1663
|
+
case "case":
|
|
1664
|
+
return ["WHEN", "THEN", "ELSE", "END"];
|
|
1665
|
+
default:
|
|
1666
|
+
return [];
|
|
1667
|
+
}
|
|
1668
|
+
};
|
|
1669
|
+
/*
|
|
1670
|
+
Helper function that takes in an index and node and determines the next
|
|
1671
|
+
delimiter position after the current index argument. This function is invoked
|
|
1672
|
+
only when we have a list of arguments via a function call or expression list.
|
|
1673
|
+
*/
|
|
1674
|
+
const getDelimiterPosition = (index, node, model, lastIndex) => {
|
|
1675
|
+
let delimiterPosition;
|
|
1676
|
+
const start = node.range.start;
|
|
1677
|
+
const end = node.range.end;
|
|
1678
|
+
/*
|
|
1679
|
+
We can ensure that the match is never null since a fully generated AST
|
|
1680
|
+
follows from a syntactically correct SQL expression.
|
|
1681
|
+
*/
|
|
1682
|
+
if (index === 0) {
|
|
1683
|
+
delimiterPosition = model.findNextMatch("(", {
|
|
1684
|
+
lineNumber: start.line,
|
|
1685
|
+
column: start.column,
|
|
1686
|
+
}, false, true, null, true).range;
|
|
1687
|
+
}
|
|
1688
|
+
else if (index === lastIndex) {
|
|
1689
|
+
delimiterPosition = {
|
|
1690
|
+
startLineNumber: end.line,
|
|
1691
|
+
endLineNumber: end.line,
|
|
1692
|
+
startColumn: end.column,
|
|
1693
|
+
endColumn: end.column,
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1696
|
+
else {
|
|
1697
|
+
delimiterPosition = model.findNextMatch(",", {
|
|
1698
|
+
lineNumber: start.line,
|
|
1699
|
+
column: start.column,
|
|
1700
|
+
}, false, true, null, true).range;
|
|
1701
|
+
}
|
|
1702
|
+
return delimiterPosition;
|
|
1703
|
+
};
|
|
1704
|
+
/* Helper function to determine if our word is a SQL parenthesis-less function. */
|
|
1705
|
+
const isSqlParenLessFunction = (word) => {
|
|
1706
|
+
const keyword = word.toUpperCase();
|
|
1707
|
+
return sqlParenLessFunctions.includes(keyword);
|
|
1708
|
+
};
|
|
1709
|
+
/*
|
|
1710
|
+
Helper function to determine if our cursor is within the range of a function.
|
|
1711
|
+
This can be any of the patterns:
|
|
1712
|
+
- function(<here>)
|
|
1713
|
+
- function(<here>
|
|
1714
|
+
<here>
|
|
1715
|
+
<here>)
|
|
1716
|
+
*/
|
|
1717
|
+
const getCursorWithinRange = (position, startPos, endPos) => {
|
|
1718
|
+
let inlineWithFunction = false;
|
|
1719
|
+
let wrappedWithinFunction = false;
|
|
1720
|
+
const cursorColumn = position.column;
|
|
1721
|
+
const cursorRow = position.lineNumber;
|
|
1722
|
+
/*
|
|
1723
|
+
Handles the case if our function is inline. An expression following the pattern
|
|
1724
|
+
|
|
1725
|
+
FUNCTION(arg) where our cursor position is in between the '(' and ')' of FUNCTION.
|
|
1726
|
+
i.e. FUNCTION( | arg | ) where '|' represents the possible cursor position.
|
|
1727
|
+
*/
|
|
1728
|
+
if (startPos.startLineNumber === endPos.startLineNumber &&
|
|
1729
|
+
startPos.startLineNumber === cursorRow &&
|
|
1730
|
+
cursorColumn > startPos.startColumn &&
|
|
1731
|
+
cursorColumn < endPos.endColumn) {
|
|
1732
|
+
inlineWithFunction = true;
|
|
1733
|
+
}
|
|
1734
|
+
/*
|
|
1735
|
+
Handles the case where our function signature is multiline. That is, if we are
|
|
1736
|
+
anywhere between the '(' and the ')' of some expression that follows the pattern:
|
|
1737
|
+
|
|
1738
|
+
FUNCTION( |
|
|
1739
|
+
| args |
|
|
1740
|
+
| )
|
|
1741
|
+
|
|
1742
|
+
where '|' represents the possible cursor position.
|
|
1743
|
+
*/
|
|
1744
|
+
const functionWrap = (cursorRow === startPos.startLineNumber && cursorColumn > startPos.startColumn) ||
|
|
1745
|
+
(cursorRow === endPos.startLineNumber && cursorColumn < endPos.endColumn) ||
|
|
1746
|
+
(cursorRow > startPos.startLineNumber && cursorRow < endPos.endLineNumber);
|
|
1747
|
+
if (startPos.startLineNumber !== endPos.startLineNumber) {
|
|
1748
|
+
if (functionWrap) {
|
|
1749
|
+
wrappedWithinFunction = true;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
return [inlineWithFunction, wrappedWithinFunction];
|
|
1753
|
+
};
|
|
1754
|
+
|
|
1755
|
+
let sqlExprWorker;
|
|
1756
|
+
/**
|
|
1757
|
+
* Returns the sql-expression worker for the model uris.
|
|
1758
|
+
* @param uris The model uris for which to get the worker.
|
|
1759
|
+
* @returns The sql-expression worker.
|
|
1760
|
+
*/
|
|
1761
|
+
async function getSqlExprWorker(...uris) {
|
|
1762
|
+
return await new Promise((resolve, reject) => {
|
|
1763
|
+
if (!sqlExprWorker) {
|
|
1764
|
+
reject(new Error("sql expression worker not registered!"));
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1767
|
+
resolve(sqlExprWorker(...uris));
|
|
1768
|
+
});
|
|
1769
|
+
}
|
|
1770
|
+
class SqlExprDiagnosticService {
|
|
1771
|
+
constructor() {
|
|
1772
|
+
this._onDiagnosticsChange = new Emitter();
|
|
1773
|
+
}
|
|
1774
|
+
/**
|
|
1775
|
+
* An event to signal changes to the diagnostics.
|
|
1776
|
+
* The event value is the uri string and the diagnostics.
|
|
1777
|
+
*/
|
|
1778
|
+
get onDiagnosticsChange() {
|
|
1779
|
+
return this._onDiagnosticsChange.event;
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1782
|
+
* Fires the diagnostics change event.
|
|
1783
|
+
* @param uri The uri of the model for which the diagnostics changed.
|
|
1784
|
+
* @param diagnostics The diagnostics for the model.
|
|
1785
|
+
*/
|
|
1786
|
+
fireDiagnosticsChange(uri, diagnostics) {
|
|
1787
|
+
this._onDiagnosticsChange.fire({ uri, diagnostics });
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
const sqlExprDiagnosticService = new SqlExprDiagnosticService();
|
|
1791
|
+
/**
|
|
1792
|
+
*
|
|
1793
|
+
* @returns The Sql Expression Diagnostic Service.
|
|
1794
|
+
*/
|
|
1795
|
+
function getSqlExprDiagnosticService() {
|
|
1796
|
+
return sqlExprDiagnosticService;
|
|
1797
|
+
}
|
|
1798
|
+
function setupMode(defaults) {
|
|
1799
|
+
const client = new SqlExprWorkerManager(defaults);
|
|
1800
|
+
const workerAccessor = async (...uris) => await client.getLanguageServiceWorker(...uris);
|
|
1801
|
+
sqlExprWorker = workerAccessor;
|
|
1802
|
+
// Use the sql-expression Monarch Json to define the highlighting
|
|
1803
|
+
languages.setMonarchTokensProvider(sqlExprDefaults.languageId, sqlExprLanguageSyntax);
|
|
1804
|
+
languages.setLanguageConfiguration(sqlExprDefaults.languageId, sqlExprLanguageConfig);
|
|
1805
|
+
const completionProvider = new SqlExprCompletionProvider(sqlExprDefaults);
|
|
1806
|
+
// Creates and registers a Completion Adapter
|
|
1807
|
+
// Registers a completion item provider for our custom language
|
|
1808
|
+
languages.registerCompletionItemProvider(sqlExprDefaults.languageId, completionProvider);
|
|
1809
|
+
// TODO: Creates and registers a Formatter
|
|
1810
|
+
// languages.registerDocumentFormattingEditProvider(
|
|
1811
|
+
// SqlExprDefaults.languageId,
|
|
1812
|
+
// new FormattingProvider(workerAccessor, SqlExprDefaults),
|
|
1813
|
+
// );
|
|
1814
|
+
// eslint-disable-next-line no-new
|
|
1815
|
+
new SqlExprDiagnosticsAdapter(defaults.languageId, workerAccessor, {
|
|
1816
|
+
defaults,
|
|
1817
|
+
diagnosticService: sqlExprDiagnosticService,
|
|
1818
|
+
});
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
export { getSqlExprDiagnosticService, getSqlExprWorker, setupMode };
|