Przeglądaj źródła

HUE-7972 [editor] Fix reporting of missing databases in the syntax checker

Johan Ahlen 7 lat temu
rodzic
commit
d8b5cec

+ 34 - 5
desktop/core/src/desktop/static/desktop/js/dataCatalog.js

@@ -281,7 +281,7 @@ var DataCatalog = (function () {
    * @param {Object} options
    * @param {string|string[]} options.path
    * @param {Object} [options.definition] - The initial definition if not already set on the entry
-   *
+   * @param {boolean} [options.cachedOnly] - Default false
    * @return {Promise}
    */
   DataCatalog.prototype.getEntry = function (options) {
@@ -306,14 +306,16 @@ var DataCatalog = (function () {
         var entry = new DataCatalogEntry(self, options.path, definition);
         if (storeEntry) {
           mergeFromStoreEntry(entry, storeEntry);
-        } else {
+        } else if (!options.cachedOnly) {
           entry.saveLater();
         }
         deferred.resolve(entry);
       }).catch(function (error) {
         console.warn(error);
         var entry = new DataCatalogEntry(self, options.path, options.definition);
-        entry.saveLater();
+        if (!options.cachedOnly) {
+          entry.saveLater();
+        }
         deferred.resolve(entry);
       })
     }
@@ -585,7 +587,7 @@ var DataCatalog = (function () {
     var deferred = $.Deferred();
 
     var sourceMetaPromise = self.getSourceMeta(options).done(function (sourceMeta) {
-      if (sourceMeta.notFound) {
+      if (!sourceMeta || sourceMeta.notFound) {
         deferred.reject();
         return;
       }
@@ -1443,7 +1445,10 @@ var DataCatalog = (function () {
 
   return {
     /**
-     * @param options
+     * @param {Object} options
+     * @param {string} options.sourceType
+     * @param {string|string[]} options.path
+     * @param {Object} [options.definition] - Optional initial definition
      *
      * @return {DataCatalogEntry}
      */
@@ -1451,6 +1456,30 @@ var DataCatalog = (function () {
       return getCatalog(options.sourceType).getEntry(options);
     },
 
+    /**
+     * This can be used as a shorthand function to get the child entries of the given path. Same as first calling
+     * getEntry then getChildren.
+     *
+     * @param {Object} options
+     * @param {string} options.sourceType
+     * @param {string|string[]} options.path
+     * @param {Object} [options.definition] - Optional initial definition of the parent entry
+     * @param {boolean} [options.silenceErrors]
+     * @param {boolean} [options.cachedOnly]
+     * @param {boolean} [options.refreshCache]
+     * @param {boolean} [options.cancellable] - Default false
+     *
+     * @return {CancellablePromise}
+     */
+    getChildren:  function(options) {
+      var deferred = $.Deferred();
+      var cancellablePromises = [];
+      getCatalog(options.sourceType).getEntry(options).done(function (entry) {
+        cancellablePromises.push(entry.getChildren(options).done(deferred.resolve).fail(deferred.reject));
+      }).fail(deferred.reject);
+      return new CancellablePromise(deferred, undefined, cancellablePromises);
+    },
+
     /**
      * @param {string} sourceType
      *

+ 35 - 45
desktop/core/src/desktop/static/desktop/js/ko.hue-bindings.js

@@ -3892,23 +3892,18 @@
       self.clearMarkedErrors();
     };
 
-    AceLocationHandler.prototype.fetchAutocompleteDeferred = function (identifierChain) {
+    AceLocationHandler.prototype.fetchChildren = function (identifierChain) {
       var self = this;
-      var promise = $.Deferred();
-      ApiHelper.getInstance().fetchAutocomplete({
+      var deferred = $.Deferred();
+      DataCatalog.getChildren({
         sourceType: self.snippet.type(),
-        identifierChain: identifierChain,
-        defaultDatabase: self.snippet.database(),
+        path: $.map(identifierChain, function (identifier) { return identifier.name }),
         silenceErrors: true,
-        cachedOnly: true,
-        successCallback: function (data) {
-          promise.resolve(data.extended_columns || data.tables_meta || []);
-        },
-        errorCallback: function () {
-          promise.reject([]);
-        }
+        cachedOnly: true
+      }).done(deferred.resolve).fail(function () {
+        deferred.reject([]);
       });
-      return promise;
+      return deferred;
     };
 
     AceLocationHandler.prototype.fetchPossibleValues = function (token) {
@@ -3918,7 +3913,7 @@
         var tablePromises = [];
         token.parseLocation.tables.forEach(function (table) {
           if (table.identifierChain) {
-            tablePromises.push(self.fetchAutocompleteDeferred(table.identifierChain));
+            tablePromises.push(self.fetchChildren(table.identifierChain));
           }
         });
         $.when.apply($, tablePromises).done(function () {
@@ -3930,7 +3925,7 @@
         }).fail(promise.reject);
       } else if (token.parseLocation.identifierChain && token.parseLocation.identifierChain.length > 0) {
         // fetch the parent
-        return self.fetchAutocompleteDeferred(token.parseLocation.identifierChain.slice(0, token.parseLocation.identifierChain.length - 1));
+        return self.fetchChildren(token.parseLocation.identifierChain.slice(0, token.parseLocation.identifierChain.length - 1));
       } else {
         promise.reject([]);
       }
@@ -3974,31 +3969,32 @@
         }
       });
 
-      var adjustColumnLocation = function (location) {
+      var resolvePathFromTables = function (location) {
         var promise = $.Deferred();
         if (location.type === 'column' && typeof location.tables !== 'undefined' && location.identifierChain.length === 1) {
           var findIdentifierChainInTable = function (tablesToGo) {
             var nextTable = tablesToGo.shift();
             if (typeof nextTable.subQuery === 'undefined') {
-              ApiHelper.getInstance().fetchAutocomplete({
+              DataCatalog.getChildren({
                 sourceType: self.snippet.type(),
-                defaultDatabase: self.snippet.database(),
-                identifierChain: nextTable.identifierChain,
+                path: $.map(nextTable.identifierChain, function (identifier) { return identifier.name }),
                 cachedOnly: true,
-                silenceErrors: true,
-                successCallback: function (data) {
-                  if (typeof data.columns !== 'undefined' && data.columns.indexOf(location.identifierChain[0].name.toLowerCase()) !== -1) {
-                    location.identifierChain = nextTable.identifierChain.concat(location.identifierChain);
-                    delete location.tables;
-                    promise.resolve();
-                  } else if (tablesToGo.length > 0) {
-                    findIdentifierChainInTable(tablesToGo);
-                  } else {
-                    promise.resolve();
-                  }
-                },
-                errorCallback: promise.resolve
-              })
+                silenceErrors: true
+              }).done(function (entries) {
+                var containsColumn = entries.some(function (entry) {
+                  return SqlUtils.identifierEquals(entry.name, location.identifierChain[0].name);
+                });
+
+                if (containsColumn) {
+                  location.identifierChain = nextTable.identifierChain.concat(location.identifierChain);
+                  delete location.tables;
+                  promise.resolve();
+                } else if (tablesToGo.length > 0) {
+                  findIdentifierChainInTable(tablesToGo);
+                } else {
+                  promise.resolve();
+                }
+              }).fail(promise.resolve);
             } else if (tablesToGo.length > 0) {
               findIdentifierChainInTable(tablesToGo);
             } else {
@@ -4036,7 +4032,7 @@
           }
         }
 
-        adjustColumnLocation(location).done(function () {
+        resolvePathFromTables(location).done(function () {
           if (location.type === 'column') {
             var possibleAlias;
             if (!location.tables && location.identifierChain && location.identifierChain.length > 1) {
@@ -4116,6 +4112,9 @@
               self.addAnchoredMarker(range,  token, 'hue-ace-syntax-warning');
             }
             verifyThrottle = window.setTimeout(verify, VERIFY_DELAY);
+          }).fail(function () {
+            // Can happen when tables aren't cached etc.
+            verifyThrottle = window.setTimeout(verify, VERIFY_DELAY);
           });
         }).fail(function () {
           // Can happen when tables aren't cached etc.
@@ -4138,7 +4137,6 @@
     AceLocationHandler.prototype.attachSqlWorker = function () {
       var self = this;
 
-      var apiHelper = ApiHelper.getInstance();
       var activeTokens = [];
 
       var lastKnownLocations = {};
@@ -4183,15 +4181,6 @@
           }
 
           if (location.identifierChain && location.identifierChain.length && location.identifierChain[0].name) {
-            // Add databases if missing in the table identifier chains
-            if (location.tables) {
-              location.tables.forEach(function (table) {
-                if (table.identifierChain && table.identifierChain.length === 1 && table.identifierChain[0].name) {
-                  table.identifierChain.unshift({ name: self.snippet.database() });
-                }
-              });
-            }
-
             // The parser isn't aware of the DDL so sometimes it marks complex columns as tables
             // I.e. "Impala SELECT a FROM b.c" Is 'b' a database or a table? If table then 'c' is complex
             if (self.snippet.type() === 'impala' &&
@@ -4247,7 +4236,8 @@
           huePubSub.publish('ace.sql.location.worker.post', {
             id: self.snippet.id(),
             statementDetails: statementDetails,
-            type: self.snippet.type()
+            type: self.snippet.type(),
+            defaultDatabase: self.snippet.database()
           });
         }
       });

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

@@ -134,6 +134,9 @@ var SqlUtils = (function () {
     locationEquals: function (a, b) {
       return a && b && a.first_line === b.first_line && a.first_column === b.first_column && a.last_line === b.last_line && a.last_column === b.last_column;
     },
+    identifierEquals: function (a, b) {
+      return a && b && a.replace(/^\s*`/, '').replace(/`\s*$/, '').toLowerCase() === b.replace(/^\s*`/, '').replace(/`\s*$/, '').toLowerCase();
+    },
     sortSuggestions: sortSuggestions
   }
 })();

+ 17 - 0
desktop/core/src/desktop/templates/ace_sql_location_worker.mako

@@ -81,6 +81,23 @@ importScripts(scriptPrefix + '${ static('desktop/js/sqlFunctions.js') }');
           this.handleStatement(statement, locations, msg.data.type, false);
         });
 
+        // Add databases where missing in the table identifier chains
+        if (msg.data.defaultDatabase) {
+          locations.forEach(function (location) {
+            if (location.identifierChain && location.identifierChain.length && location.identifierChain[0].name) {
+              if (location.tables) {
+                location.tables.forEach(function (table) {
+                  if (table.identifierChain && table.identifierChain.length === 1 && table.identifierChain[0].name) {
+                    table.identifierChain.unshift({ name: msg.data.defaultDatabase });
+                  }
+                });
+              } else if (location.type === 'table' && location.identifierChain.length === 1) {
+                location.identifierChain.unshift({ name: msg.data.defaultDatabase });
+              }
+            }
+          });
+        }
+
         postMessage({
           id: msg.data.id,
           editorChangeTime: msg.data.statementDetails.editorChangeTime,