浏览代码

HUE-4197 [editor] Autocomplete for DROP TABLE, DATABASE and SCHEMA

This also adds database suggestions to the editor.
Johan Ahlen 9 年之前
父节点
当前提交
eacfe0d

+ 102 - 3
desktop/core/src/desktop/static/desktop/js/autocomplete/sql.jison

@@ -32,10 +32,11 @@
 'BOOLEAN'                           { return 'BOOLEAN'; }
 'BY'                                { return 'BY'; }
 'CHAR'                              { return 'CHAR'; }
-'CREATE'                            { return 'CREATE'; }
+'CREATE'                            { determineCase(yytext); return 'CREATE'; }
 'DATABASE'                          { return 'DATABASE'; }
 'DECIMAL'                           { return 'DECIMAL'; }
 'DOUBLE'                            { return 'DOUBLE'; }
+'DROP'                              { determineCase(yytext); return 'DROP'; }
 'EXISTS'                            { return 'EXISTS'; }
 'FLOAT'                             { return 'FLOAT'; }
 'FROM'                              { return 'FROM'; }
@@ -49,6 +50,7 @@
 'ON'                                { return 'ON'; }
 'OR'                                { return 'OR'; }
 'ORDER'                             { return 'ORDER'; }
+'ROLE'                              { return 'ROLE'; }
 'SCHEMA'                            { return 'SCHEMA'; }
 'SELECT'                            { determineCase(yytext); return 'SELECT'; }
 'SET'                               { return 'SET'; }
@@ -69,22 +71,30 @@
 <hive>'DATA'                        { return '<hive>DATA'; }
 <hive>'DATE'                        { return '<hive>DATE'; }
 <hive>'EXTERNAL'                    { return '<hive>EXTERNAL'; }
+<hive>'FUNCTION'                    { return '<hive>FUNCTION'; }
+<hive>'INDEX'                       { return '<hive>INDEX'; }
 <hive>'INPATH'                      { this.begin('hdfs'); return '<hive>INPATH'; }
 <hive>'LATERAL'                     { return '<hive>LATERAL'; }
 <hive>'LOAD'                        { return '<hive>LOAD'; }
 <hive>'LOCATION'                    { this.begin('hdfs'); return '<hive>LOCATION'; }
+<hive>'MACRO'                       { return '<hive>MACRO'; }
+<hive>'TEMPORARY'                   { return '<hive>TEMPORARY'; }
 
 <hive>'explode'                     { return '<hive>explode'; }
 <hive>'posexplode'                     { return '<hive>posexplode'; }
 
 <hive>[.]                           { return '<hive>.'; }
 
+<impala>'AGGREGATE'                 { return '<impala>AGGREGATE'; }
 <impala>'COMMENT'                   { return '<impala>COMMENT'; }
 <impala>'DATA'                      { return '<impala>DATA'; }
+<impala>'FUNCTION'                  { return '<impala>FUNCTION'; }
 <impala>'EXTERNAL'                  { return '<impala>EXTERNAL'; }
+<impala>'INCREMENTAL'               { return '<impala>INCREMENTAL'; }
 <impala>'INPATH'                    { this.begin('hdfs'); return '<impala>INPATH'; }
 <impala>'LOAD'                      { return '<impala>LOAD'; }
 <impala>'LOCATION'                  { this.begin('hdfs'); return '<impala>LOCATION'; }
+<impala>'STATS'                     { return '<impala>STATS'; }
 
 <impala>[.]                         { return '<impala>.'; }
 
@@ -186,8 +196,7 @@ SqlStatements
  ;
 
 SqlStatement
- : UseStatement
- | DataManipulation
+ : DataManipulation
  | DataDefinition
  | QueryExpression
  | 'REGULAR_IDENTIFIER' 'PARTIAL_CURSOR' 'REGULAR_IDENTIFIER'
@@ -332,6 +341,12 @@ ValueExpression
  ;
 
 DataDefinition
+ : CreateStatement
+ | DropStatement
+ | UseStatement
+ ;
+
+CreateStatement
  : TableDefinition
  | DatabaseDefinition
  | 'CREATE' PartialIdentifierOrCursor
@@ -344,11 +359,93 @@ DataDefinition
    }
  ;
 
+DropStatement
+ : 'DROP' PartialIdentifierOrCursor
+   {
+     if (parser.yy.dialect === 'hive') {
+       suggestKeywords(['DATABASE', 'FUNCTION', 'INDEX', 'MACRO', 'ROLE', 'SCHEMA', 'TABLE', 'TEMPORARY FUNCTION', 'TEMPORARY MACRO', 'VIEW']);
+     } else if (parser.yy.dialect === 'impala') {
+       suggestKeywords(['AGGREGATE FUNCTION', 'DATABASE', 'FUNCTION', 'INCREMENTAL STATS', 'ROLE', 'SCHEMA', 'STATS', 'TABLE', 'VIEW']);
+     } else {
+       suggestKeywords(['ROLE', 'SCHEMA', 'TABLE', 'VIEW']);
+     }
+   }
+ | DropDatabaseStatement
+ | DropTableStatement
+ ;
+
+OptionalHiveCascadeOrRestrict
+ :
+ | '<hive>CASCADE'
+ | '<hive>RESTRICT'
+ ;
+
+DropDatabaseStatement
+ : 'DROP' DatabaseOrSchema OptionalIfExists
+ | 'DROP' DatabaseOrSchema OptionalIfExists PartialIdentifierOrCursor
+   {
+     if (!$3) {
+       suggestKeywords(['IF EXISTS']);
+     }
+     suggestDatabases();
+   }
+ | 'DROP' DatabaseOrSchema OptionalIfExists RegularOrBacktickedIdentifier PartialIdentifierOrCursor
+   {
+     if (parser.yy.dialect === 'hive') {
+       suggestKeywords(['CASCADE', 'RESTRICT']);
+     }
+   }
+ | 'DROP' DatabaseOrSchema OptionalIfExists RegularOrBacktickedIdentifier OptionalHiveCascadeOrRestrict
+ ;
+
+DropTableStatement
+ : 'DROP' 'TABLE' OptionalIfExists
+ | 'DROP' 'TABLE' OptionalIfExists PartialIdentifierOrCursor
+   {
+     if (!$3) {
+       suggestKeywords(['IF EXISTS']);
+     }
+     suggestTables();
+     suggestDatabases({
+       appendDot: true
+     });
+   }
+ | 'DROP' 'TABLE' OptionalIfExists TablePrimary
+   {
+     if (!$3 && !$4.partial) {
+       suggestKeywords(['IF EXISTS']);
+     }
+     if ($4.partial) {
+       if ($4.identifierChain.length === 1) {
+         suggestTablesOrColumns($4.identifierChain[0].name);
+       } else if ($1.identifierChain.length === 0) {
+         suggestTables();
+         suggestDatabases({ appendDot: true });
+       }
+     }
+   }
+ | 'DROP' 'TABLE' OptionalIfExists TablePrimary 'CURSOR'
+   {
+     if (parser.yy.dialect === 'hive') {
+       suggestKeywords(['PURGE']);
+     }
+   }
+ ;
+
 DatabaseOrSchema
  : 'DATABASE'
  | 'SCHEMA'
  ;
 
+OptionalIfExists
+ : { $$ = false }
+ | 'IF' PartialIdentifierOrCursor
+   {
+     suggestKeywords(['EXISTS']);
+   }
+ | 'IF' 'EXISTS'
+ ;
+
 OptionalIfNotExists
  :
  | 'IF' PartialIdentifierOrCursor
@@ -1415,6 +1512,8 @@ var lexerModified = false;
  */
 parser.parseSql = function(beforeCursor, afterCursor, dialect) {
   parser.yy.activeDialect = dialect;
+  parser.yy.result = {};
+  parser.yy.lowerCase = false;
 
   // Hack to set the inital state of the lexer without first having to hit a token
   // has to be done as the first token found can be dependant on dialect

文件差异内容过多而无法显示
+ 0 - 1
desktop/core/src/desktop/static/desktop/js/autocomplete/sql.js


+ 22 - 3
desktop/core/src/desktop/static/desktop/js/sqlAutocompleter2.js

@@ -43,7 +43,7 @@
 
     if (parseResult.suggestKeywords) {
       parseResult.suggestKeywords.forEach(function (keyword) {
-        completions.push({ value: keyword, meta: 'keyword' });
+        completions.push({ value: parseResult.lowerCase ? keyword.toLowerCase() : keyword, meta: 'keyword' });
       });
     }
 
@@ -57,11 +57,30 @@
       completions.push({ value: '*', meta: 'keyword' });
     }
 
-    if (parseResult.suggestHdfs || parseResult.suggestTables || parseResult.suggestColumns || parseResult.suggestValues) {
+    if (parseResult.suggestDatabases || parseResult.suggestHdfs || parseResult.suggestTables || parseResult.suggestColumns || parseResult.suggestValues) {
       var database = parseResult.useDatabase || self.snippet.database();
 
       var deferrals = [];
 
+      if (parseResult.suggestDatabases) {
+        var databaseDeferred = $.Deferred();
+        deferrals.push(databaseDeferred);
+
+        self.snippet.getApiHelper().loadDatabases({
+          sourceType: self.snippet.type(),
+          successCallback: function (data) {
+            data.forEach(function (db) {
+              completions.push({ value: db + (parseResult.suggestDatabases.appendDot ? '.' : ''), meta: 'database' });
+            });
+            databaseDeferred.resolve();
+
+          },
+          silenceErrors: true,
+          errorCallback: databaseDeferred.resolve
+        })
+
+      }
+
       if (parseResult.suggestHdfs) {
         var parts = parseResult.suggestHdfs.path.split('/');
         // Drop the first " or '
@@ -261,7 +280,7 @@
     callback(completions);
   };
 
-  var typeOrder = { 'star': 1, 'alias': 2, 'table': 3, 'identifier': 4, 'key' : 5, 'value' : 6, 'keyword': 7 };
+  var typeOrder = { 'star': 1, 'alias': 2, 'table': 3, 'database': 4, 'identifier': 5, 'key' : 6, 'value' : 7, 'keyword': 8 };
 
   SqlAutocompleter2.prototype.sortCompletions = function (completions) {
     completions.sort(function (a, b) {

+ 187 - 0
desktop/core/src/desktop/static/desktop/spec/autocomplete/sqlSpec.js

@@ -865,6 +865,193 @@ define([
       })
     });
 
+    describe('drop statements', function () {
+      it('should suggest keywords after DROP', function() {
+        assertAutoComplete({
+          beforeCursor: 'DROP ',
+          afterCursor: '',
+          expectedResult: {
+            lowerCase: false,
+            suggestKeywords: ['ROLE', 'SCHEMA', 'TABLE', 'VIEW']
+          }
+        });
+      });
+
+      describe('hive specific', function () {
+        it('should suggest keywords after DROP', function() {
+          assertAutoComplete({
+            beforeCursor: 'DROP ',
+            afterCursor: '',
+            dialect: 'hive',
+            expectedResult: {
+              lowerCase: false,
+              suggestKeywords: ['DATABASE', 'FUNCTION', 'INDEX', 'MACRO', 'ROLE', 'SCHEMA', 'TABLE', 'TEMPORARY FUNCTION', 'TEMPORARY MACRO', 'VIEW']
+            }
+          });
+        });
+      });
+
+      describe('hive specific', function () {
+        it('should follow case after drop', function() {
+          assertAutoComplete({
+            beforeCursor: 'drop ',
+            afterCursor: '',
+            dialect: 'hive',
+            expectedResult: {
+              lowerCase: true,
+              suggestKeywords: ['DATABASE', 'FUNCTION', 'INDEX', 'MACRO', 'ROLE', 'SCHEMA', 'TABLE', 'TEMPORARY FUNCTION', 'TEMPORARY MACRO', 'VIEW']
+            }
+          });
+        });
+      });
+
+      describe('impala specific', function () {
+        it('should suggest keywords after DROP', function() {
+          assertAutoComplete({
+            beforeCursor: 'DROP ',
+            afterCursor: '',
+            dialect: 'impala',
+            expectedResult: {
+              lowerCase: false,
+              suggestKeywords: ['AGGREGATE FUNCTION', 'DATABASE', 'FUNCTION', 'INCREMENTAL STATS', 'ROLE', 'SCHEMA', 'STATS', 'TABLE', 'VIEW']
+            }
+          });
+        });
+      });
+
+      describe('drop database statements', function () {
+        it('should suggest databases after DROP DATABASE ', function() {
+          assertAutoComplete({
+            beforeCursor: 'DROP DATABASE ',
+            afterCursor: '',
+            dialect: 'hive',
+            expectedResult: {
+              lowerCase: false,
+              suggestDatabases: {},
+              suggestKeywords: ['IF EXISTS']
+            }
+          });
+        });
+
+        it('should suggest databases after DROP SCHEMA ', function() {
+          assertAutoComplete({
+            beforeCursor: 'DROP SCHEMA ',
+            afterCursor: '',
+            expectedResult: {
+              lowerCase: false,
+              suggestDatabases: {},
+              suggestKeywords: ['IF EXISTS']
+            }
+          });
+        });
+
+        it('should suggest keywords after DROP DATABASE IF ', function() {
+          assertAutoComplete({
+            beforeCursor: 'DROP DATABASE IF ',
+            afterCursor: '',
+            dialect: 'impala',
+            expectedResult: {
+              lowerCase: false,
+              suggestKeywords: ['EXISTS']
+            }
+          });
+        });
+
+        it('should suggest databases after DROP DATABASE IF EXISTS ', function() {
+          assertAutoComplete({
+            beforeCursor: 'DROP DATABASE IF EXISTS ',
+            afterCursor: '',
+            dialect: 'hive',
+            expectedResult: {
+              lowerCase: false,
+              suggestDatabases: {}
+            }
+          });
+        });
+
+        describe('Hive specific', function () {
+          it('should suggest keywords after DROP DATABASE foo ', function() {
+            assertAutoComplete({
+              beforeCursor: 'DROP DATABASE foo ',
+              afterCursor: '',
+              dialect: 'hive',
+              expectedResult: {
+                lowerCase: false,
+                suggestKeywords: ['CASCADE', 'RESTRICT']
+              }
+            });
+          });
+        });
+      });
+
+      describe('drop table statements', function () {
+        it('should suggest tables after DROP TABLE ', function() {
+          assertAutoComplete({
+            beforeCursor: 'DROP TABLE ',
+            afterCursor: '',
+            expectedResult: {
+              lowerCase: false,
+              suggestTables: {},
+              suggestKeywords: ['IF EXISTS'],
+              suggestDatabases: {
+                appendDot: true
+              }
+            }
+          });
+        });
+
+        it('should suggest tables after DROP TABLE db. ', function() {
+          assertAutoComplete({
+            beforeCursor: 'DROP TABLE db.',
+            afterCursor: '',
+            expectedResult: {
+              lowerCase: false,
+              suggestTables: { database: 'db' }
+            }
+          });
+        });
+
+        it('should suggest keywords after DROP TABLE IF ', function() {
+          assertAutoComplete({
+            beforeCursor: 'DROP TABLE IF ',
+            afterCursor: '',
+            expectedResult: {
+              lowerCase: false,
+              suggestKeywords: ['EXISTS']
+            }
+          });
+        });
+
+        it('should suggest tables after DROP TABLE IF EXISTS ', function() {
+          assertAutoComplete({
+            beforeCursor: 'DROP TABLE IF EXISTS ',
+            afterCursor: '',
+            expectedResult: {
+              lowerCase: false,
+              suggestTables: {},
+              suggestDatabases: {
+                appendDot: true
+              }
+            }
+          });
+        });
+
+        describe('Hive specific', function () {
+          it('should suggest keywords after DROP TABLE foo ', function() {
+            assertAutoComplete({
+              beforeCursor: 'DROP TABLE foo ',
+              afterCursor: '',
+              dialect: 'hive',
+              expectedResult: {
+                lowerCase: false,
+                suggestKeywords: ['PURGE']
+              }
+            });
+          });
+        });
+      });
+    });
+
     describe('update statements', function () {
       it('should suggest tables after UPDATE', function() {
         assertAutoComplete({

部分文件因为文件数量过多而无法显示