|
@@ -38,19 +38,50 @@ var SqlAutocompleter3 = (function () {
|
|
|
JOIN: -1
|
|
JOIN: -1
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- var sortByWeight = function (suggestions) {
|
|
|
|
|
- suggestions.sort(function (a, b) {
|
|
|
|
|
- if (typeof a.weight !== 'undefined' && typeof b.weight !== 'undefined' && b.weight !== a.weight) {
|
|
|
|
|
- return b.weight - a.weight;
|
|
|
|
|
- }
|
|
|
|
|
- if (typeof a.weight !== 'undefined' && typeof b.weight === 'undefined') {
|
|
|
|
|
- return -1;
|
|
|
|
|
- }
|
|
|
|
|
- if (typeof b.weight !== 'undefined' && typeof a.weight === 'undefined') {
|
|
|
|
|
- return 1;
|
|
|
|
|
- }
|
|
|
|
|
- return a.value.localeCompare(b.value);
|
|
|
|
|
- })
|
|
|
|
|
|
|
+
|
|
|
|
|
+ var hiveReservedKeywords = {
|
|
|
|
|
+ ALL: true, ALTER: true, AND: true, ARRAY: true, AS: true, AUTHORIZATION: true, BETWEEN: true, BIGINT: true, BINARY: true, BOOLEAN: true, BOTH: true, BY: true, CASE: true, CAST: true,
|
|
|
|
|
+ CHAR: true, COLUMN: true, CONF: true, CREATE: true, CROSS: true, CUBE: true, CURRENT: true, CURRENT_DATE: true, CURRENT_TIMESTAMP: true, CURSOR: true,
|
|
|
|
|
+ DATABASE: true, DATE: true, DECIMAL: true, DELETE: true, DESCRIBE: true, DISTINCT: true, DOUBLE: true, DROP: true, ELSE: true, END: true, EXCHANGE: true, EXISTS: true,
|
|
|
|
|
+ EXTENDED: true, EXTERNAL: true, FALSE: true, FETCH: true, FLOAT: true, FOLLOWING: true, FOR: true, FROM: true, FULL: true, FUNCTION: true, GRANT: true, GROUP: true,
|
|
|
|
|
+ GROUPING: true, HAVING: true, IF: true, IMPORT: true, IN: true, INNER: true, INSERT: true, INT: true, INTERSECT: true, INTERVAL: true, INTO: true, IS: true, JOIN: true, LATERAL: true,
|
|
|
|
|
+ LEFT: true, LESS: true, LIKE: true, LOCAL: true, MACRO: true, MAP: true, MORE: true, NONE: true, NOT: true, NULL: true, OF: true, ON: true, OR: true, ORDER: true, OUT: true, OUTER: true, OVER: true,
|
|
|
|
|
+ PARTIALSCAN: true, PARTITION: true, PERCENT: true, PRECEDING: true, PRESERVE: true, PROCEDURE: true, RANGE: true, READS: true, REDUCE: true,
|
|
|
|
|
+ REGEXP: true, REVOKE: true, RIGHT: true, RLIKE: true, ROLLUP: true, ROW: true, ROWS: true,
|
|
|
|
|
+ SELECT: true, SET: true, SMALLINT: true, TABLE: true, TABLESAMPLE: true, THEN: true, TIMESTAMP: true, TO: true, TRANSFORM: true, TRIGGER: true, TRUE: true,
|
|
|
|
|
+ TRUNCATE: true, UNBOUNDED: true, UNION: true, UNIQUEJOIN: true, UPDATE: true, USER: true, USING: true, VALUES: true, VARCHAR: true, WHEN: true, WHERE: true,
|
|
|
|
|
+ WINDOW: true, WITH: true
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ var extraHiveReservedKeywords = {
|
|
|
|
|
+ ASC: true, CLUSTER: true, DESC: true, DISTRIBUTE: true, FORMATTED: true, FUNCTION: true, INDEX: true, INDEXES: true, LIMIT: true, LOCK: true, SCHEMA: true, SORT: true
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ var impalaReservedKeywords = {
|
|
|
|
|
+ ADD: true, AGGREGATE: true, ALL: true, ALTER: true, AND: true, API_VERSION: true, AS: true, ASC: true, AVRO: true, BETWEEN: true, BIGINT: true, BINARY: true, BOOLEAN: true, BY: true, CACHED: true, CASE: true, CAST: true, CHANGE: true, CHAR: true, CLASS: true, CLOSE_FN: true,
|
|
|
|
|
+ COLUMN: true, COLUMNS: true, COMMENT: true, COMPUTE: true, CREATE: true, CROSS: true, DATA: true, DATABASE: true, DATABASES: true, DATE: true, DATETIME: true, DECIMAL: true, DELIMITED: true, DESC: true, DESCRIBE: true, DISTINCT: true, DIV: true, DOUBLE: true, DROP: true, ELSE: true, END: true,
|
|
|
|
|
+ ESCAPED: true, EXISTS: true, EXPLAIN: true, EXTERNAL: true, FALSE: true, FIELDS: true, FILEFORMAT: true, FINALIZE_FN: true, FIRST: true, FLOAT: true, FORMAT: true, FORMATTED: true, FROM: true, FULL: true, FUNCTION: true, FUNCTIONS: true, GROUP: true, HAVING: true, IF: true, IN: true, INCREMENTAL: true,
|
|
|
|
|
+ INIT_FN: true, INNER: true, INPATH: true, INSERT: true, INT: true, INTEGER: true, INTERMEDIATE: true, INTERVAL: true, INTO: true, INVALIDATE: true, IS: true, JOIN: true, LAST: true, LEFT: true, LIKE: true, LIMIT: true, LINES: true, LOAD: true, LOCATION: true, MERGE_FN: true, METADATA: true,
|
|
|
|
|
+ NOT: true, NULL: true, NULLS: true, OFFSET: true, ON: true, OR: true, ORDER: true, OUTER: true, OVERWRITE: true, PARQUET: true, PARQUETFILE: true, PARTITION: true, PARTITIONED: true, PARTITIONS: true, PREPARE_FN: true, PRODUCED: true, RCFILE: true, REAL: true, REFRESH: true, REGEXP: true, RENAME: true,
|
|
|
|
|
+ REPLACE: true, RETURNS: true, RIGHT: true, RLIKE: true, ROW: true, SCHEMA: true, SCHEMAS: true, SELECT: true, SEMI: true, SEQUENCEFILE: true, SERDEPROPERTIES: true, SERIALIZE_FN: true, SET: true, SHOW: true, SMALLINT: true, STATS: true, STORED: true, STRAIGHT_JOIN: true, STRING: true, SYMBOL: true, TABLE: true,
|
|
|
|
|
+ TABLES: true, TBLPROPERTIES: true, TERMINATED: true, TEXTFILE: true, THEN: true, TIMESTAMP: true, TINYINT: true, TO: true, TRUE: true, UNCACHED: true, UNION: true, UPDATE_FN: true, USE: true, USING: true, VALUES: true, VIEW: true, WHEN: true, WHERE: true, WITH: true,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ var backTickIfNeeded = function (sourceType, text) {
|
|
|
|
|
+ if (text.indexOf('`') === 0) {
|
|
|
|
|
+ return text;
|
|
|
|
|
+ }
|
|
|
|
|
+ var upperText = text.toUpperCase();
|
|
|
|
|
+ if (sourceType === 'hive' && (hiveReservedKeywords[upperText] || extraHiveReservedKeywords[upperText])) {
|
|
|
|
|
+ return '`' + text + '`';
|
|
|
|
|
+ } else if (sourceType === 'impala' && impalaReservedKeywords[upperText]) {
|
|
|
|
|
+ return '`' + text + '`';
|
|
|
|
|
+ } else if (impalaReservedKeywords[upperText] || hiveReservedKeywords[upperText] || extraHiveReservedKeywords[upperText]) {
|
|
|
|
|
+ return '`' + text + '`';
|
|
|
|
|
+ } else if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(text)) {
|
|
|
|
|
+ return '`' + text + '`';
|
|
|
|
|
+ }
|
|
|
|
|
+ return text;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -71,39 +102,36 @@ var SqlAutocompleter3 = (function () {
|
|
|
/**
|
|
/**
|
|
|
* Represents the keyword category of suggestions
|
|
* Represents the keyword category of suggestions
|
|
|
*
|
|
*
|
|
|
- * @param parseResult
|
|
|
|
|
|
|
+ * @param parent
|
|
|
* @param colRefDeferral
|
|
* @param colRefDeferral
|
|
|
* @constructor
|
|
* @constructor
|
|
|
*/
|
|
*/
|
|
|
- function KeywordsCategory (parseResult, colRefDeferral, sourceType) {
|
|
|
|
|
|
|
+ function KeywordsCategory (parent, colRefDeferral) {
|
|
|
var self = this;
|
|
var self = this;
|
|
|
self.label = AutocompleterGlobals.i18n.keywords;
|
|
self.label = AutocompleterGlobals.i18n.keywords;
|
|
|
self.suggestions = ko.observableArray([]);
|
|
self.suggestions = ko.observableArray([]);
|
|
|
self.loading = ko.observable(false);
|
|
self.loading = ko.observable(false);
|
|
|
- self.sourceType = sourceType;
|
|
|
|
|
|
|
|
|
|
- if (parseResult.suggestKeywords) {
|
|
|
|
|
- var keywordSuggestions = $.map(parseResult.suggestKeywords, function (keyword) {
|
|
|
|
|
- return new Suggestion(parseResult.lowerCase ? keyword.value.toLowerCase() : keyword.value, AutocompleterGlobals.i18n.meta.keyword, keyword.weight);
|
|
|
|
|
|
|
+ if (parent.parseResult.suggestKeywords) {
|
|
|
|
|
+ var keywordSuggestions = $.map(parent.parseResult.suggestKeywords, function (keyword) {
|
|
|
|
|
+ return new Suggestion(parent.parseResult.lowerCase ? keyword.value.toLowerCase() : keyword.value, AutocompleterGlobals.i18n.meta.keyword, keyword.weight);
|
|
|
});
|
|
});
|
|
|
- sortByWeight(keywordSuggestions);
|
|
|
|
|
self.suggestions(keywordSuggestions);
|
|
self.suggestions(keywordSuggestions);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (parseResult.suggestColRefKeywords) {
|
|
|
|
|
|
|
+ if (parent.parseResult.suggestColRefKeywords) {
|
|
|
self.loading(true);
|
|
self.loading(true);
|
|
|
// We have to wait for the column reference type to be resolved
|
|
// We have to wait for the column reference type to be resolved
|
|
|
colRefDeferral.done(function (colRef) {
|
|
colRefDeferral.done(function (colRef) {
|
|
|
if (colRef !== null) {
|
|
if (colRef !== null) {
|
|
|
var keywordSuggestions = self.suggestions();
|
|
var keywordSuggestions = self.suggestions();
|
|
|
- Object.keys(parseResult.suggestColRefKeywords).forEach(function (typeForKeywords) {
|
|
|
|
|
- if (SqlFunctions.matchesType(self.sourceType, [typeForKeywords], [type.toUpperCase()])) {
|
|
|
|
|
- parseResult.suggestColRefKeywords[typeForKeywords].forEach(function (keyword) {
|
|
|
|
|
- keywordSuggestions.push(new Suggestion(parseResult.lowerCase ? keyword.toLowerCase() : keyword, AutocompleterGlobals.i18n.meta.keyword, DEFAULT_WEIGHTS.COLREF_KEYWORD));
|
|
|
|
|
|
|
+ Object.keys(parent.parseResult.suggestColRefKeywords).forEach(function (typeForKeywords) {
|
|
|
|
|
+ if (SqlFunctions.matchesType(sourceType, [typeForKeywords], [type.toUpperCase()])) {
|
|
|
|
|
+ parent.parseResult.suggestColRefKeywords[typeForKeywords].forEach(function (keyword) {
|
|
|
|
|
+ keywordSuggestions.push(new Suggestion(parent.parseResult.lowerCase ? keyword.toLowerCase() : keyword, AutocompleterGlobals.i18n.meta.keyword, DEFAULT_WEIGHTS.COLREF_KEYWORD));
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
- sortByWeight(keywordSuggestions);
|
|
|
|
|
self.suggestions(keywordSuggestions);
|
|
self.suggestions(keywordSuggestions);
|
|
|
}
|
|
}
|
|
|
self.loading(false);
|
|
self.loading(false);
|
|
@@ -111,6 +139,49 @@ var SqlAutocompleter3 = (function () {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * @param parent
|
|
|
|
|
+ * @constructor
|
|
|
|
|
+ */
|
|
|
|
|
+ function TablesCategory (parent) {
|
|
|
|
|
+ var self = this;
|
|
|
|
|
+ self.label = AutocompleterGlobals.i18n.keywords;
|
|
|
|
|
+ self.suggestions = ko.observableArray([]);
|
|
|
|
|
+ self.loading = ko.observable(true);
|
|
|
|
|
+
|
|
|
|
|
+ var prefix = parent.parseResult.suggestTables.prependQuestionMark ? '? ' : '';
|
|
|
|
|
+ if (parent.parseResult.suggestTables.prependFrom) {
|
|
|
|
|
+ prefix += parent.parseResult.lowerCase ? 'from ' : 'FROM ';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ parent.apiHelper.fetchTables({
|
|
|
|
|
+ sourceType: parent.sourceType,
|
|
|
|
|
+ databaseName: parent.parseResult.suggestTables.identifierChain && parent.parseResult.suggestTables.identifierChain.length === 1 ? parent.parseResult.suggestTables.identifierChain[0].name : parent.defaultDatabase,
|
|
|
|
|
+ successCallback: function (data) {
|
|
|
|
|
+ var tables = [];
|
|
|
|
|
+ data.tables_meta.forEach(function (tablesMeta) {
|
|
|
|
|
+ if (parent.parseResult.suggestTables.onlyTables && tablesMeta.type.toLowerCase() !== 'table' ||
|
|
|
|
|
+ parent.parseResult.suggestTables.onlyViews && tablesMeta.type.toLowerCase() !== 'view') {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ tables.push(new Suggestion(prefix + backTickIfNeeded(parent.sourceType, tablesMeta.name), AutocompleterGlobals.i18n.meta[tablesMeta.type.toLowerCase()], DEFAULT_WEIGHTS.TABLE));
|
|
|
|
|
+ });
|
|
|
|
|
+ self.loading(false);
|
|
|
|
|
+ self.suggestions(tables);
|
|
|
|
|
+ },
|
|
|
|
|
+ silenceErrors: true,
|
|
|
|
|
+ errorCallback: function () {
|
|
|
|
|
+ self.loading(false);
|
|
|
|
|
+ },
|
|
|
|
|
+ timeout: parent.timeout
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param options
|
|
|
|
|
+ * @constructor
|
|
|
|
|
+ */
|
|
|
function Suggestions (options) {
|
|
function Suggestions (options) {
|
|
|
var self = this;
|
|
var self = this;
|
|
|
self.apiHelper = ApiHelper.getInstance();
|
|
self.apiHelper = ApiHelper.getInstance();
|
|
@@ -121,11 +192,64 @@ var SqlAutocompleter3 = (function () {
|
|
|
self.callback = options.callback;
|
|
self.callback = options.callback;
|
|
|
|
|
|
|
|
var colRefDeferral = self.handleColumnReference();
|
|
var colRefDeferral = self.handleColumnReference();
|
|
|
- self.keywords = new KeywordsCategory(options.parseResult, colRefDeferral);
|
|
|
|
|
|
|
+ var allDbsDeferral = self.loadAllDatabases();
|
|
|
|
|
+
|
|
|
|
|
+ self.handleKeywords(colRefDeferral);
|
|
|
|
|
+ self.handleTables(allDbsDeferral);
|
|
|
|
|
|
|
|
colRefDeferral.done(self.callback)
|
|
colRefDeferral.done(self.callback)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ Suggestions.prototype.loadAllDatabases = function () {
|
|
|
|
|
+ var self = this;
|
|
|
|
|
+ var dbsDeferral = $.Deferred();
|
|
|
|
|
+ self.apiHelper.loadDatabases({
|
|
|
|
|
+ sourceType: self.sourceType,
|
|
|
|
|
+ successCallback: dbsDeferral.resolve,
|
|
|
|
|
+ timeout: self.timeout,
|
|
|
|
|
+ silenceErrors: true,
|
|
|
|
|
+ errorCallback: function () {
|
|
|
|
|
+ dbsDeferral.resolve([]);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ return dbsDeferral;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ Suggestions.prototype.handleKeywords = function (colRefDeferral) {
|
|
|
|
|
+ var self = this;
|
|
|
|
|
+ if (self.parseResult.suggestKeywords || self.parseResult.suggestColRefKeywords) {
|
|
|
|
|
+ self.keywords = new KeywordsCategory(self, colRefDeferral);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ Suggestions.prototype.handleTables = function (allDbsDeferral, colRefDeferral) {
|
|
|
|
|
+ var self = this;
|
|
|
|
|
+ if (self.parseResult.suggestTables) {
|
|
|
|
|
+ if (self.sourceType == 'impala' && self.parseResult.suggestTables.identifierChain && self.parseResult.suggestTables.identifierChain.length === 1) {
|
|
|
|
|
+ allDbsDeferral.done(function (databases) {
|
|
|
|
|
+ var foundDb = databases.filter(function (db) {
|
|
|
|
|
+ return db.toLowerCase() === self.parseResult.suggestTables.identifierChain[0].name.toLowerCase();
|
|
|
|
|
+ });
|
|
|
|
|
+ if (foundDb.length > 0) {
|
|
|
|
|
+ self.tables = new TablesCategory(self);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ self.parseResult.suggestColumns = { tables: [{ identifierChain: self.parseResult.suggestTables.identifierChain }] };
|
|
|
|
|
+ self.handleColumns(colRefDeferral);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ } else if (self.sourceType == 'impala' && self.parseResult.suggestTables.identifierChain && self.parseResult.suggestTables.identifierChain.length > 1) {
|
|
|
|
|
+ self.parseResult.suggestColumns = { tables: [{ identifierChain: self.parseResult.suggestTables.identifierChain }] };
|
|
|
|
|
+ self.handleColumns(colRefDeferral);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ self.tables = new TablesCategory(self);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ Suggestions.prototype.handleColumns = function (colRefDeferral) {
|
|
|
|
|
+
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* For some suggestions the column type is needed, for instance with functions we should only suggest
|
|
* For some suggestions the column type is needed, for instance with functions we should only suggest
|
|
|
* columns that matches the argument type, cos(|) etc.
|
|
* columns that matches the argument type, cos(|) etc.
|