瀏覽代碼

HUE-6065 [assist] Improve the insert at cursor functionality

Backtick when required and smarter statement generation.
Johan Ahlen 8 年之前
父節點
當前提交
ce6996fadb

+ 54 - 22
desktop/core/src/desktop/static/desktop/js/assist/assistDbEntry.js

@@ -126,33 +126,59 @@ var AssistDbEntry = (function () {
 
     self.editorText = ko.pureComputed(function () {
       if (self.definition.isTable || self.definition.isView) {
-        return self.definition.name;
+        return self.getTableName();
       }
       if (self.definition.isColumn) {
-        return self.definition.name + ", ";
+        return self.getColumnName() + ', ';
       }
-      var parts = [];
-      var entry = self;
-      while (entry != null) {
-        if (entry.definition.isTable || self.definition.isView) {
-          break;
-        }
-        if (entry.definition.isArray || entry.definition.isMapValue) {
-          if (self.assistDbSource.sourceType === 'hive') {
-            parts.push("[]");
-          }
-        } else {
-          parts.push(entry.definition.name);
-          parts.push(".");
-        }
-        entry = entry.parent;
-      }
-      parts.reverse();
-      parts.push(", ");
-      return parts.slice(1).join("");
+      return self.getComplexName() + ', ';
     });
   }
 
+  var findNameInHierarchy = function (entry, searchCondition) {
+    var sourceType = entry.sourceType;
+    while (entry && !searchCondition(entry)) {
+      entry = entry.parent;
+    }
+    if (entry) {
+      return SqlUtils.backTickIfNeeded(sourceType, entry.definition.name);
+    }
+  };
+
+  AssistDbEntry.prototype.getDatabaseName = function () {
+    return findNameInHierarchy(this, function (entry) { return entry.definition.isDatabase });
+  };
+
+  AssistDbEntry.prototype.getTableName = function () {
+    return findNameInHierarchy(this, function (entry) { return entry.definition.isTable || entry.definition.isView });
+  };
+
+  AssistDbEntry.prototype.getColumnName = function () {
+    return findNameInHierarchy(this, function (entry) { return entry.definition.isColumn });
+  };
+
+  AssistDbEntry.prototype.getComplexName = function () {
+    var entry = self;
+    var sourceType = self.sourceType;
+    var parts = [];
+    while (entry != null) {
+      if (entry.definition.isTable || entry.definition.isView) {
+        break;
+      }
+      if (entry.definition.isArray || entry.definition.isMapValue) {
+        if (sourceType === 'hive') {
+          parts.push("[]");
+        }
+      } else {
+        parts.push(SqlUtils.backTickIfNeeded(sourceType, entry.definition.name));
+        parts.push(".");
+      }
+      entry = entry.parent;
+    }
+    parts.reverse();
+    return parts.slice(1).join("");
+  };
+
   AssistDbEntry.prototype.showContextPopover = function (entry, event, positionAdjustment) {
     var self = this;
     var $source = $(event.target);
@@ -450,7 +476,13 @@ var AssistDbEntry = (function () {
 
   AssistDbEntry.prototype.dblClick = function () {
     var self = this;
-    huePubSub.publish('assist.dblClickDbItem', self);
+    if (self.definition.isTable || self.definition.isView) {
+      huePubSub.publish('editor.insert.table.at.cursor', { name: self.getTableName(), database: self.getDatabaseName() });
+    } else if (self.definition.isColumn) {
+      huePubSub.publish('editor.insert.column.at.cursor', { name: self.getColumnName(), table: self.getTableName(), database: self.getDatabaseName() });
+    } else {
+      huePubSub.publish('editor.insert.column.at.cursor', { name: self.getComplexName(), table: self.getTableName(), database: self.getDatabaseName() });
+    }
   };
 
   AssistDbEntry.prototype.openInMetastore = function () {

+ 23 - 11
desktop/core/src/desktop/static/desktop/js/ko.hue-bindings.js

@@ -3830,18 +3830,30 @@
         exec: editor.commands.commands['gotoline'].exec
       });
 
-      huePubSub.subscribe("assist.dblClickDbItem", function(assistDbEntry) {
-        if ($el.data("last-active-editor")) {
-          var text = assistDbEntry.editorText();
-          if (editor.getValue() == "") {
-            if (assistDbEntry.definition.isTable) {
-              text = "SELECT * FROM " + assistDbEntry.editorText() + " LIMIT 100";
-            }
-            else if (assistDbEntry.definition.isColumn) {
-              text = "SELECT " + assistDbEntry.editorText().split(",")[0] + " FROM " + assistDbEntry.parent.editorText() + " LIMIT 100";
-            }
+
+      var isNewStatement = function () {
+        return /^\s*$/.test(editor.getValue()) || /^.*;\s*$/.test(editor.getTextBeforeCursor());
+      };
+
+      huePubSub.subscribe('editor.insert.table.at.cursor', function(details) {
+        if ($el.data('last-active-editor')) {
+          var qualifiedName = snippet.database() == details.database ? details.name : details.database + '.' + details.name;
+          if (isNewStatement()) {
+            editor.session.insert(editor.getCursorPosition(), 'SELECT * FROM ' + qualifiedName + ' LIMIT 100;');
+          } else {
+            editor.session.insert(editor.getCursorPosition(), ' ' + qualifiedName + ' ');
+          }
+        }
+      });
+
+      huePubSub.subscribe('editor.insert.column.at.cursor', function(details) {
+        if ($el.data('last-active-editor')) {
+          if (isNewStatement()) {
+            var qualifiedFromName = snippet.database() == details.database ? details.table : details.database + '.' + details.table;
+            editor.session.insert(editor.getCursorPosition(), 'SELECT '  + details.name + ' FROM ' + qualifiedFromName + ' LIMIT 100;');
+          } else {
+            editor.session.insert(editor.getCursorPosition(), ' ' + details.name + ' ');
           }
-          editor.session.insert(editor.getCursorPosition(), text);
         }
       });
 

+ 19 - 65
desktop/core/src/desktop/static/desktop/js/sqlAutocompleter3.js

@@ -56,34 +56,6 @@ var AutocompleteResults = (function () {
 
   var POPULAR_CATEGORIES = [CATEGORIES.POPULAR_AGGREGATE, CATEGORIES.POPULAR_GROUP_BY, CATEGORIES.POPULAR_ORDER_BY, CATEGORIES.POPULAR_FILTER, CATEGORIES.POPULAR_ACTIVE_JOIN, CATEGORIES.POPULAR_JOIN_CONDITION, CATEGORIES.POPULAR_JOIN];
 
-  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, KEY: true, KUDU: 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, PRIMARY: 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 adjustWeightsBasedOnPopularity = function(suggestions, totalPopularity) {
     suggestions.forEach(function (suggestion) {
       suggestion.details.popularity.relativePopularity = Math.round(100 * suggestion.details.popularity.popularity / totalPopularity);
@@ -265,24 +237,6 @@ var AutocompleteResults = (function () {
     }).extend({ rateLimit: 200 });
   }
 
-  AutocompleteResults.prototype.backTickIfNeeded = function (text) {
-    var self = this;
-    if (text.indexOf('`') === 0) {
-      return text;
-    }
-    var upperText = text.toUpperCase();
-    if (self.snippet.type() === 'hive' && (hiveReservedKeywords[upperText] || extraHiveReservedKeywords[upperText])) {
-      return '`' + text + '`';
-    } else if (self.snippet.type() === '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;
-  };
-
   AutocompleteResults.prototype.update = function (parseResult) {
     var self = this;
 
@@ -566,7 +520,7 @@ var AutocompleteResults = (function () {
       databasesDeferred.done(function (dbs) {
         dbs.forEach(function (db) {
           databaseSuggestions.push({
-            value: prefix + self.backTickIfNeeded(db) + (suggestDatabases.appendDot ? '.' : ''),
+            value: prefix + SqlUtils.backTickIfNeeded(self.snippet.type(), db) + (suggestDatabases.appendDot ? '.' : ''),
             filterValue: db,
             meta: AutocompleterGlobals.i18n.meta.database,
             category: CATEGORIES.DATABASE,
@@ -608,7 +562,7 @@ var AutocompleteResults = (function () {
               var details = tableMeta;
               details.database = database;
               tableSuggestions.push({
-                value: prefix + self.backTickIfNeeded(tableMeta.name),
+                value: prefix + SqlUtils.backTickIfNeeded(self.snippet.type(), tableMeta.name),
                 filterValue: tableMeta.name,
                 tableName: tableMeta.name,
                 meta: AutocompleterGlobals.i18n.meta[tableMeta.type.toLowerCase()],
@@ -716,7 +670,7 @@ var AutocompleteResults = (function () {
               var type = typeof column.type !== 'undefined' && column.type !== 'COLREF' ? column.type : 'T';
               if (typeof column.alias !== 'undefined') {
                 columnSuggestions.push({
-                  value: self.backTickIfNeeded(column.alias),
+                  value: SqlUtils.backTickIfNeeded(self.snippet.type(), column.alias),
                   filterValue: column.alias,
                   meta: type,
                   category: CATEGORIES.COLUMN,
@@ -726,7 +680,7 @@ var AutocompleteResults = (function () {
                 })
               } else if (typeof column.identifierChain !== 'undefined' && column.identifierChain.length > 0 && typeof column.identifierChain[column.identifierChain.length - 1].name !== 'undefined') {
                 columnSuggestions.push({
-                  value: self.backTickIfNeeded(column.identifierChain[column.identifierChain.length - 1].name),
+                  value: SqlUtils.backTickIfNeeded(self.snippet.type(), column.identifierChain[column.identifierChain.length - 1].name),
                   filterValue: column.identifierChain[column.identifierChain.length - 1].name,
                   meta: type,
                   category: CATEGORIES.COLUMN,
@@ -752,7 +706,7 @@ var AutocompleteResults = (function () {
             var type = typeof column.type !== 'undefined' && column.type !== 'COLREF' ? column.type : 'T';
             if (column.alias) {
               columnSuggestions.push({
-                value: self.backTickIfNeeded(column.alias),
+                value: SqlUtils.backTickIfNeeded(self.snippet.type(), column.alias),
                 filterValue: column.alias,
                 meta: type,
                 category: CATEGORIES.COLUMN,
@@ -762,7 +716,7 @@ var AutocompleteResults = (function () {
               })
             } else if (column.identifierChain && column.identifierChain.length > 0) {
               columnSuggestions.push({
-                value: self.backTickIfNeeded(column.identifierChain[column.identifierChain.length - 1].name),
+                value: SqlUtils.backTickIfNeeded(self.snippet.type(), column.identifierChain[column.identifierChain.length - 1].name),
                 filterValue: column.identifierChain[column.identifierChain.length - 1].name,
                 meta: type,
                 category: CATEGORIES.COLUMN,
@@ -792,7 +746,7 @@ var AutocompleteResults = (function () {
             column.identifierChain = data.identifierChain;
             if (column.type.indexOf('map') === 0 && self.snippet.type() === 'hive') {
               columnSuggestions.push({
-                value: self.backTickIfNeeded(column.name) + '[]',
+                value: SqlUtils.backTickIfNeeded(self.snippet.type(), column.name) + '[]',
                 filterValue: column.name,
                 meta: 'map',
                 category: CATEGORIES.COLUMN,
@@ -802,7 +756,7 @@ var AutocompleteResults = (function () {
               })
             } else if (column.type.indexOf('map') === 0) {
               columnSuggestions.push({
-                value: self.backTickIfNeeded(column.name),
+                value: SqlUtils.backTickIfNeeded(self.snippet.type(), column.name),
                 filterValue: column.name,
                 meta: 'map',
                 category: CATEGORIES.COLUMN,
@@ -812,7 +766,7 @@ var AutocompleteResults = (function () {
               })
             } else if (column.type.indexOf('struct') === 0) {
               columnSuggestions.push({
-                value: self.backTickIfNeeded(column.name),
+                value: SqlUtils.backTickIfNeeded(self.snippet.type(), column.name),
                 filterValue: column.name,
                 meta: 'struct',
                 category: CATEGORIES.COLUMN,
@@ -822,7 +776,7 @@ var AutocompleteResults = (function () {
               })
             } else if (column.type.indexOf('array') === 0 && self.snippet.type() === 'hive') {
               columnSuggestions.push({
-                value: self.backTickIfNeeded(column.name) + '[]',
+                value: SqlUtils.backTickIfNeeded(self.snippet.type(), column.name) + '[]',
                 filterValue: column.name,
                 meta: 'array',
                 category: CATEGORIES.COLUMN,
@@ -832,7 +786,7 @@ var AutocompleteResults = (function () {
               })
             } else if (column.type.indexOf('array') === 0) {
               columnSuggestions.push({
-                value: self.backTickIfNeeded(column.name),
+                value: SqlUtils.backTickIfNeeded(self.snippet.type(), column.name),
                 filterValue: column.name,
                 meta: 'array',
                 category: CATEGORIES.COLUMN,
@@ -842,7 +796,7 @@ var AutocompleteResults = (function () {
               })
             } else if (types[0].toUpperCase() !== 'T' && types.filter(function (type) { return type.toUpperCase() === column.type.toUpperCase() }).length > 0) {
               columnSuggestions.push({
-                value: self.backTickIfNeeded(column.name),
+                value: SqlUtils.backTickIfNeeded(self.snippet.type(), column.name),
                 filterValue: column.name,
                 meta: column.type,
                 category: CATEGORIES.COLUMN,
@@ -854,7 +808,7 @@ var AutocompleteResults = (function () {
             } else if (SqlFunctions.matchesType(self.snippet.type(), types, [column.type.toUpperCase()]) ||
                 SqlFunctions.matchesType(self.snippet.type(), [column.type.toUpperCase()], types)) {
               columnSuggestions.push({
-                value: self.backTickIfNeeded(column.name),
+                value: SqlUtils.backTickIfNeeded(self.snippet.type(), column.name),
                 filterValue: column.name,
                 meta: column.type,
                 category: CATEGORIES.COLUMN,
@@ -870,7 +824,7 @@ var AutocompleteResults = (function () {
             column.table = data.table;
             column.identifierChain = data.identifierChain;
             columnSuggestions.push({
-              value: self.backTickIfNeeded(column),
+              value: SqlUtils.backTickIfNeeded(self.snippet.type(), column),
               filterValue: column,
               meta: 'column',
               category: CATEGORIES.COLUMN,
@@ -905,7 +859,7 @@ var AutocompleteResults = (function () {
             field.identifierChain = data.identifierChain;
 
             columnSuggestions.push({
-              value: self.backTickIfNeeded(field.name),
+              value: SqlUtils.backTickIfNeeded(self.snippet.type(), field.name),
               filterValue: field.name,
               meta: field.type,
               category: CATEGORIES.COLUMN,
@@ -923,7 +877,7 @@ var AutocompleteResults = (function () {
             if (SqlFunctions.matchesType(self.snippet.type(), types, [field.type.toUpperCase()]) ||
                 SqlFunctions.matchesType(self.snippet.type(), [field.type.toUpperCase()], types)) {
               columnSuggestions.push({
-                value: self.backTickIfNeeded(field.name),
+                value: SqlUtils.backTickIfNeeded(self.snippet.type(), field.name),
                 filterValue: field.name,
                 meta: field.type,
                 category: CATEGORIES.COLUMN,
@@ -943,7 +897,7 @@ var AutocompleteResults = (function () {
               if ((field.type === 'array' || field.type === 'map')) {
                 if (self.snippet.type() === 'hive') {
                   columnSuggestions.push({
-                    value: self.backTickIfNeeded(field.name) + '[]',
+                    value: SqlUtils.backTickIfNeeded(self.snippet.type(), field.name) + '[]',
                     filterValue: field.name,
                     meta: field.type,
                     category: CATEGORIES.COLUMN,
@@ -953,7 +907,7 @@ var AutocompleteResults = (function () {
                   });
                 } else {
                   columnSuggestions.push({
-                    value: self.backTickIfNeeded(field.name),
+                    value: SqlUtils.backTickIfNeeded(self.snippet.type(), field.name),
                     filterValue: field.name,
                     meta: field.type,
                     category: CATEGORIES.COLUMN,
@@ -965,7 +919,7 @@ var AutocompleteResults = (function () {
               } else if (SqlFunctions.matchesType(self.snippet.type(), types, [field.type.toUpperCase()]) ||
                   SqlFunctions.matchesType(self.snippet.type(), [field.type.toUpperCase()], types)) {
                 columnSuggestions.push({
-                  value: self.backTickIfNeeded(field.name),
+                  value: SqlUtils.backTickIfNeeded(self.snippet.type(), field.name),
                   filterValue: field.name,
                   meta: field.type,
                   category: CATEGORIES.COLUMN,

+ 70 - 0
desktop/core/src/desktop/static/desktop/js/sqlUtils.js

@@ -0,0 +1,70 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+var SqlUtils = (function () {
+
+  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, KEY: true, KUDU: 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, PRIMARY: 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
+  };
+
+  return {
+    backTickIfNeeded: function (sourceType, identifier) {
+      if (identifier.indexOf('`') === 0) {
+        return identifier;
+      }
+      var upperIdentifier = identifier.toUpperCase();
+      if (sourceType === 'hive' && (hiveReservedKeywords[upperIdentifier] || extraHiveReservedKeywords[upperIdentifier])) {
+        return '`' + identifier + '`';
+      }
+      if (sourceType === 'impala' && impalaReservedKeywords[upperIdentifier]) {
+        return '`' + identifier + '`';
+      }
+      if ((sourceType !== 'impala' && sourceType !== 'hive') && (impalaReservedKeywords[upperIdentifier] || hiveReservedKeywords[upperIdentifier] || extraHiveReservedKeywords[upperIdentifier])) {
+        return '`' + identifier + '`';
+      }
+      if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(identifier)) {
+        return '`' + identifier + '`';
+      }
+      return identifier;
+    }
+  }
+
+})();

+ 2 - 6
desktop/core/src/desktop/templates/assist.mako

@@ -85,7 +85,7 @@ from notebook.conf import get_ordered_interpreters
   <script type="text/html" id="sql-context-items">
     <!-- ko if: typeof definition !== 'undefined' -->
     <li><a href="javascript:void(0);" data-bind="click: function (data) { showContextPopover(data, { target: $parentContext.$contextSourceElement }, { left: 4, top: 2 }); }"><i class="fa fa-fw fa-info"></i> ${ _('Show details') }</a></li>
-    <!-- ko if: definition.isView || definition.isTable -->
+    <!-- ko if: !definition.isDatabase -->
     <li><a href="javascript:void(0);" data-bind="click: dblClick"><i class="fa fa-fw fa-paste"></i> ${ _('Insert at cursor') }</a></li>
     <!-- /ko -->
     <!-- ko if: definition.isView || definition.isTable || definition.isDatabase -->
@@ -174,7 +174,7 @@ from notebook.conf import get_ordered_interpreters
       </a>
       <!-- /ko -->
       <!-- ko ifnot: expandable -->
-      <div style="cursor: default;" class="assist-entry assist-field-link" href="javascript:void(0)" data-bind="event: { dblClick: dblClick }, attr: {'title': definition.title }">
+      <div style="cursor: default;" class="assist-entry assist-field-link" href="javascript:void(0)" data-bind="event: { dblclick: dblClick }, attr: {'title': definition.title }">
         <span class="highlightable" data-bind="css: { 'highlight': highlight}, attr: {'column': columnName, 'table': tableName, 'database': databaseName}, text: definition.displayName, draggableText: { text: editorText, meta: {'type': 'sql', 'column': columnName, 'table': tableName, 'database': databaseName} }"></span><!-- ko if: definition.primary_key === 'true'  --> <i class="fa fa-key"></i><!-- /ko --><!-- ko if: assistDbSource.activeSort() === 'popular' && popularity() > 0 --> <i title="${ _('Popular') }" class="fa fa-star-o top-star"></i> <!-- /ko -->
       </div>
       <!-- /ko -->
@@ -1257,10 +1257,6 @@ from notebook.conf import get_ordered_interpreters
           self.showCores(currentEntry.hasOnlyCores());
         };
 
-        huePubSub.subscribe('assist.clickCollectionItem', function (entry) {
-
-        });
-
         huePubSub.subscribe('assist.dblClickCollectionItem', function (entry) {
           window.open('/indexer/#edit/' + entry.definition.name);
         });

+ 1 - 0
desktop/core/src/desktop/templates/common_header.mako

@@ -150,6 +150,7 @@ if USE_NEW_EDITOR.get():
   <script src="${ static('desktop/ext/js/knockout.validation.min.js') }"></script>
   <script src="${ static('desktop/js/ko.switch-case.js') }"></script>
   <script src="${ static('desktop/js/ko.hue-bindings.js') }"></script>
+  <script src="${ static('desktop/js/sqlUtils.js') }"></script>
   <script src="${ static('desktop/ext/js/dropzone.min.js') }"></script>
 
   ${ koComponents.all() }

+ 2 - 2
desktop/core/src/desktop/templates/common_header_footer_components.mako

@@ -183,7 +183,7 @@ from metadata.conf import has_optimizer, OPTIMIZER
         return mTime;
       }
       return mTime;
-    }
+    };
 
     //Add CSRF Token to all XHR Requests
     var xrhsend = XMLHttpRequest.prototype.send;
@@ -195,7 +195,7 @@ from metadata.conf import has_optimizer, OPTIMIZER
     %endif
 
       return xrhsend.apply(this, arguments);
-    }
+    };
 
     $.fn.dataTableExt.sErrMode = "throw";
 

+ 1 - 0
desktop/core/src/desktop/templates/responsive.mako

@@ -502,6 +502,7 @@ ${ hueIcons.symbols() }
 <script src="${ static('desktop/js/ko.editable.js') }"></script>
 <script src="${ static('desktop/js/ko.switch-case.js') }"></script>
 <script src="${ static('desktop/js/ko.hue-bindings.js') }"></script>
+<script src="${ static('desktop/js/sqlUtils.js') }"></script>
 <script src="${ static('desktop/js/jquery.scrollleft.js') }"></script>
 <script src="${ static('desktop/js/jquery.scrollup.js') }"></script>
 <script src="${ static('desktop/js/jquery.tour.js') }"></script>