瀏覽代碼

HUE-4276 [editor] Autocomplete around arithmetic operations and support BETWEEN, EXISTS, RLIKE and REGEX

Johan Ahlen 9 年之前
父節點
當前提交
88a7aca

+ 163 - 73
desktop/core/src/desktop/static/desktop/js/autocomplete/sql.jison

@@ -16,7 +16,7 @@
 
 %lex
 %options case-insensitive
-%s hive impala
+%s between hive impala
 %x hdfs DoubleQuotedValue SingleQuotedValue backtickedValue
 %%
 
@@ -132,11 +132,14 @@
 <impala>\[                          { return '<impala>['; }
 <impala>\]                          { return '<impala>]'; }
 
+<between>'AND'                      { this.popState(); return 'BETWEEN_AND'; }
+
 'ALL'                               { return 'ALL'; }
 'AND'                               { return 'AND'; }
 'ANY'                               { return 'ANY'; }
 'AS'                                { return 'AS'; }
 'ASC'                               { return 'ASC'; }
+'BETWEEN'                           { this.begin('between'); return 'BETWEEN'; }
 'BIGINT'                            { return 'BIGINT'; }
 'BOOLEAN'                           { return 'BOOLEAN'; }
 'BY'                                { return 'BY'; }
@@ -158,6 +161,8 @@
 'OUTER'                             { return 'OUTER'; }
 'INNER'                             { return 'INNER'; }
 'RIGHT'                             { return 'RIGHT'; }
+'RLIKE'                             { return 'RLIKE'; }
+'REGEXP'                            { return 'REGEXP'; }
 'FULL'                              { return 'FULL'; }
 'GROUP'                             { return 'GROUP'; }
 'GROUPING'                          { return 'GROUPING'; }
@@ -172,6 +177,7 @@
 'MAX'                               { return 'MAX'; }
 'MIN'                               { return 'MIN'; }
 'NOT'                               { return 'NOT'; }
+'NULL'                              { return 'NULL'; }
 'ON'                                { return 'ON'; }
 'OR'                                { return 'OR'; }
 'ORDER'                             { return 'ORDER'; }
@@ -211,8 +217,28 @@
 <hdfs>[']                           { this.popState(); return 'HDFS_END_QUOTE'; }
 <hdfs><<EOF>>                       { return 'EOF'; }
 
-[-+&~|^/%*(),.;!]                   { return yytext; }
-[=<>]+                              { return yytext; }
+'&&'                                { return 'AND'; }
+'||'                                { return 'OR'; }
+
+'='                                 { return '='; }
+'!='                                { return 'COMPARISON_OPERATOR'; }
+'<'                                 { return 'COMPARISON_OPERATOR'; }
+'<='                                { return 'COMPARISON_OPERATOR'; }
+'<=>'                               { return 'COMPARISON_OPERATOR'; }
+'<>'                                { return 'COMPARISON_OPERATOR'; }
+'>='                                { return 'COMPARISON_OPERATOR'; }
+'>'                                 { return 'COMPARISON_OPERATOR'; }
+
+'-'                                 { return '-'; }
+'*'                                 { return '*'; }
+'+'                                 { return 'ARITHMETIC_OPERATOR'; }
+'/'                                 { return 'ARITHMETIC_OPERATOR'; }
+'%'                                 { return 'ARITHMETIC_OPERATOR'; }
+'|'                                 { return 'ARITHMETIC_OPERATOR'; }
+'^'                                 { return 'ARITHMETIC_OPERATOR'; }
+'&'                                 { return 'ARITHMETIC_OPERATOR'; }
+
+[~(),.;!]                           { return yytext; }
 
 \[                                  { return '['; }
 \]                                  { return ']'; }
@@ -240,15 +266,14 @@
 
 /* operators and precedence levels */
 
-%left 'OR'
-%left 'AND'
-%nonassoc 'IN' 'IS' 'LIKE'
-%left 'NOT' '!'
-%left  '=' '<>' '<' '>' '<=' '>=' '<=>'
-%left '|'
-%left '&'
-%left '+' '-'
-%left '*' '/' '%' 'MOD'
+%left 'AND' 'OR'
+%left 'BETWEEN'
+%left 'NOT' '!' '~'
+%left '=' 'COMPARISON_OPERATOR'
+%left '-' '*' 'ARITHMETIC_OPERATOR'
+
+%nonassoc 'CURSOR' 'PARTIAL_CURSOR'
+%nonassoc 'IN' 'IS' 'LIKE' 'RLIKE' 'REGEXP' 'EXISTS'
 
 %start Sql
 
@@ -1432,7 +1457,18 @@ OptionalLimitClause_EDIT
 SearchCondition
  : ValueExpression
    {
-     $$ = { suggestKeywords: ['<', '<=', '<>', '=', '>', '>=', 'AND', 'NOT IN', 'IN', 'OR'] };
+     $$ = { suggestKeywords: ['<', '<=', '<>', '=', '>', '>=', 'AND', 'BETWEEN', 'IN', 'NOT BETWEEN', 'NOT IN', 'OR'] };
+     if (isHive()) {
+       $$.suggestKeywords.push('<=>');
+     }
+     if ($1.columnReference) {
+       $$.suggestKeywords.push('EXISTS');
+       $$.suggestKeywords.push('LIKE');
+       $$.suggestKeywords.push('NOT EXISTS');
+       $$.suggestKeywords.push('NOT LIKE');
+       $$.suggestKeywords.push('RLIKE');
+       $$.suggestKeywords.push('REGEX');
+     }
    }
  ;
 
@@ -1442,90 +1478,131 @@ SearchCondition_EDIT
 
 ValueExpression
  : NonParenthesizedValueExpressionPrimary
+// | NumericValueFunction
  | 'NOT' ValueExpression
+ | '~' ValueExpression
  | '-' ValueExpression
- | ValueExpression InClause
- | ValueExpression 'IS' OptionalNot TruthValue
- | ValueExpression '=' ValueExpression
- | ValueExpression '<=' ValueExpression
- | ValueExpression '>=' ValueExpression
- | ValueExpression '<' ValueExpression
- | ValueExpression '>' ValueExpression
- | ValueExpression '<>' ValueExpression
- | '(' ValueExpression ')'
-// | NumericValueFunction
- | ValueExpression '+' ValueExpression
- | ValueExpression '-' ValueExpression
- | ValueExpression '*' ValueExpression
- | ValueExpression '/' ValueExpression
- | ValueExpression 'OR' ValueExpression
- | ValueExpression 'AND' ValueExpression
+ | ValueExpression 'NOT' 'LIKE' SingleQuotedValue
+ | ValueExpression 'LIKE' SingleQuotedValue
+ | ValueExpression 'RLIKE' SingleQuotedValue
+ | ValueExpression 'REGEXP' SingleQuotedValue
+ | ValueExpression 'NOT' 'EXISTS' TableSubquery
+ | ValueExpression 'EXISTS' TableSubquery
+ | '(' ValueExpression ')' { $$ = {}; }
+ | ValueExpression 'IS' OptionalNot TruthValueOrNull { $$ = {}; }
+ | ValueExpression '=' ValueExpression { $$ = {}; }
+ | ValueExpression 'COMPARISON_OPERATOR' ValueExpression { $$ = {}; }
+ | ValueExpression '-' ValueExpression { $$ = {}; }
+ | ValueExpression '*' ValueExpression { $$ = {}; }
+ | ValueExpression 'ARITHMETIC_OPERATOR' ValueExpression { $$ = {}; }
+ | ValueExpression 'OR' ValueExpression { $$ = {}; }
+ | ValueExpression 'AND' ValueExpression { $$ = {}; }
+ | ValueExpression InClause { $$ = {}; }
+ ;
+
+ValueExpression
+ : ValueExpression 'BETWEEN' ValueExpression 'BETWEEN_AND' ValueExpression { $$ = {}; }
  ;
 
 ValueExpression_EDIT
  : NonParenthesizedValueExpressionPrimary_EDIT
  | 'NOT' ValueExpression_EDIT
+ | '~' ValueExpression_EDIT
  | '-' ValueExpression_EDIT
- | ValueExpression InClause_EDIT
- | ValueExpression 'IS' OptionalNot 'CURSOR'
- | ValueExpression 'IS' OptionalNot 'CURSOR' TruthValue
- | ValueExpression ComparisonOperators AnyCursor
+ | ValueExpression_EDIT 'NOT' 'LIKE' SingleQuotedValue
+ | ValueExpression_EDIT 'LIKE' SingleQuotedValue
+ | ValueExpression_EDIT 'RLIKE' SingleQuotedValue
+ | ValueExpression_EDIT 'REGEXP' SingleQuotedValue
+ | ValueExpression_EDIT 'NOT' 'EXISTS' TableSubquery
+ | ValueExpression_EDIT 'EXISTS' TableSubquery
+ | ValueExpression 'NOT' 'EXISTS' TableSubquery_EDIT
+ | ValueExpression 'EXISTS' TableSubquery_EDIT
+ | '(' ValueExpression_EDIT ')'
+ | '(' ValueExpression_EDIT error
+ | ValueExpression 'IS' 'NOT' 'CURSOR'
    {
-     if ($1.columnReference) {
-       suggestValues({ identifierChain: $1.columnReference });
-     }
-     suggestColumns();
+     suggestKeywords(['FALSE', 'NULL', 'TRUE']);
    }
- | 'CURSOR' ComparisonOperators ValueExpression
+ | ValueExpression 'IS' 'CURSOR'
    {
-     if ($3.columnReference) {
-       suggestValues({ identifierChain: $3.columnReference });
-     }
-     suggestColumns();
+     suggestKeywords(['FALSE', 'NOT FALSE', 'NOT NULL', 'NOT TRUE', 'NULL', 'TRUE']);
    }
- | ValueExpression NumericExpressionOperator AnyCursor
+ | ValueExpression 'IS' 'CURSOR' TruthValue
    {
-     suggestColumns();
+     suggestKeywords(['NOT']);
    }
- | ValueExpression AndOrOr 'CURSOR'
+ | ValueExpression 'NOT' 'CURSOR'
    {
-     suggestColumns();
+     suggestKeywords(['BETWEEN', 'EXISTS', 'IN', 'LIKE']);
    }
+ | ValueExpression InClause_EDIT
  ;
 
-InClause
- : 'NOT' 'IN' TableSubquery
- | 'IN' TableSubquery
- ;
-
-InClause_EDIT
- : 'NOT' 'CURSOR'
+ValueExpression_EDIT
+ : ValueExpression_EDIT 'BETWEEN' ValueExpression 'BETWEEN_AND' ValueExpression
+ | ValueExpression 'BETWEEN' ValueExpression_EDIT 'BETWEEN_AND' ValueExpression
+ | ValueExpression 'BETWEEN' ValueExpression 'BETWEEN_AND' ValueExpression_EDIT
+ | ValueExpression 'BETWEEN' ValueExpression 'BETWEEN_AND' 'CURSOR'
    {
-     suggestKeywords(['IN']);
+     valueExpressionSuggest($1);
+   }
+ | ValueExpression 'BETWEEN' ValueExpression 'CURSOR'
+   {
+     suggestKeywords(['AND']);
+   }
+ | ValueExpression 'BETWEEN' 'CURSOR'
+   {
+     valueExpressionSuggest($1);
    }
- | 'NOT' 'IN' TableSubquery_EDIT
- | 'IN' TableSubquery_EDIT
  ;
 
-NumericExpressionOperator
- : '+'
- | '-'
- | '*'
- | '/'
+ValueExpression_EDIT
+ : ValueExpression '=' ValueExpression_EDIT
+ | ValueExpression 'COMPARISON_OPERATOR' ValueExpression_EDIT
+ | ValueExpression '-' ValueExpression_EDIT
+ | ValueExpression '*' ValueExpression_EDIT
+ | ValueExpression 'ARITHMETIC_OPERATOR' ValueExpression_EDIT
+ | ValueExpression 'OR' ValueExpression_EDIT
+ | ValueExpression 'AND' ValueExpression_EDIT
+ | ValueExpression '=' RightPart_EDIT { valueExpressionSuggest($1) }
+ | ValueExpression 'COMPARISON_OPERATOR' RightPart_EDIT { valueExpressionSuggest($1) }
+ | ValueExpression '-' RightPart_EDIT { suggestColumns() }
+ | ValueExpression '*' RightPart_EDIT { suggestColumns() }
+ | ValueExpression 'ARITHMETIC_OPERATOR' RightPart_EDIT { suggestColumns() }
+ | ValueExpression 'OR' RightPart_EDIT { suggestColumns() }
+ | ValueExpression 'AND' RightPart_EDIT { suggestColumns() }
  ;
 
-AndOrOr
- : 'AND'
- | 'OR'
+ValueExpression_EDIT
+ : ValueExpression_EDIT '=' ValueExpression
+ | ValueExpression_EDIT 'COMPARISON_OPERATOR' ValueExpression
+ | ValueExpression_EDIT '-' ValueExpression
+ | ValueExpression_EDIT '*' ValueExpression
+ | ValueExpression_EDIT 'ARITHMETIC_OPERATOR' ValueExpression
+ | ValueExpression_EDIT 'OR' ValueExpression
+ | ValueExpression_EDIT 'AND' ValueExpression
+ | 'CURSOR' '=' ValueExpression { valueExpressionSuggest($3) }
+ | 'CURSOR' 'COMPARISON_OPERATOR' ValueExpression { valueExpressionSuggest($3) }
+// | 'CURSOR' '-' ValueExpression { suggestColumns() }
+ | 'CURSOR' '*' ValueExpression { suggestColumns() }
+ | 'CURSOR' 'ARITHMETIC_OPERATOR' ValueExpression { suggestColumns() }
+ | 'CURSOR' 'OR' ValueExpression { suggestColumns() }
+ | 'CURSOR' 'AND' ValueExpression { suggestColumns() }
+ ;
+
+RightPart_EDIT
+ : AnyCursor
+ | PartialBacktickedIdentifier
  ;
 
-ComparisonOperators
- : '='
- | '<'
- | '>'
- | '>='
- | '<='
- | '<>'
+InClause
+ : 'NOT' 'IN' TableSubquery
+ | 'IN' TableSubquery
+ ;
+
+InClause_EDIT
+ : 'NOT' 'IN' TableSubquery_EDIT
+ | 'IN' TableSubquery_EDIT
  ;
 
 // TODO: Expand with more choices
@@ -1580,6 +1657,12 @@ TruthValue
  | 'FALSE'
  ;
 
+TruthValueOrNull
+ : 'TRUE'
+ | 'FALSE'
+ | 'NULL'
+ ;
+
 OptionalNot
  :
  | 'NOT'
@@ -3017,6 +3100,13 @@ var mergeSuggestKeywords = function() {
   return {}
 }
 
+var valueExpressionSuggest = function(other) {
+  if (other.columnReference) {
+    suggestValues({ identifierChain: other.columnReference });
+  }
+  suggestColumns();
+}
+
 var prioritizeSuggestions = function () {
   parser.yy.result.lowerCase = parser.yy.lowerCase || false;
   if (typeof parser.yy.result.suggestIdentifiers !== 'undefined' &&  parser.yy.result.suggestIdentifiers.length > 0) {
@@ -3087,8 +3177,8 @@ parser.expandImpalaIdentifierChain = function (tablePrimaries, identifierChain)
 };
 
 parser.identifyPartials = function (beforeCursor, afterCursor) {
-  var beforeMatch = beforeCursor.match(/[a-zA-Z_]*$/);
-  var afterMatch = afterCursor.match(/^[a-zA-Z_]*/);
+  var beforeMatch = beforeCursor.match(/[0-9a-zA-Z_]*$/);
+  var afterMatch = afterCursor.match(/^[0-9a-zA-Z_]*/);
   return { left: beforeMatch ? beforeMatch[0].length : 0, right: afterMatch ? afterMatch[0].length : 0}
 };
 

File diff suppressed because it is too large
+ 0 - 0
desktop/core/src/desktop/static/desktop/js/autocomplete/sql.js


+ 6 - 1
desktop/core/src/desktop/static/desktop/js/sqlAutocompleter2.js

@@ -61,6 +61,11 @@
       var deferrals = [];
 
       if (parseResult.suggestDatabases) {
+        var prefix = parseResult.suggestDatabases.prependQuestionMark ? '? ' : '';
+        if (parseResult.suggestDatabases.prependFrom) {
+          prefix += parseResult.lowerCase ? 'from ' : 'FROM ';
+        }
+
         var databaseDeferred = $.Deferred();
         deferrals.push(databaseDeferred);
 
@@ -68,7 +73,7 @@
           sourceType: self.snippet.type(),
           successCallback: function (data) {
             data.forEach(function (db) {
-              completions.push({ value: db + (parseResult.suggestDatabases.appendDot ? '.' : ''), meta: 'database' });
+              completions.push({ value: prefix + db + (parseResult.suggestDatabases.appendDot ? '.' : ''), meta: 'database' });
             });
             databaseDeferred.resolve();
 

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

@@ -132,6 +132,7 @@ define([
         expect(sql.identifyPartials('', '')).toEqual({left: 0, right: 0});
         expect(sql.identifyPartials('foo', '')).toEqual({left: 3, right: 0});
         expect(sql.identifyPartials(' foo', '')).toEqual({left: 3, right: 0});
+        expect(sql.identifyPartials('asdf 1234', '')).toEqual({left: 4, right: 0});
         expect(sql.identifyPartials('foo', 'bar')).toEqual({left: 3, right: 3});
         expect(sql.identifyPartials('foo ', '')).toEqual({left: 0, right: 0});
         expect(sql.identifyPartials('foo \'', '\'')).toEqual({left: 0, right: 0});

+ 88 - 7
desktop/core/src/desktop/static/desktop/spec/autocomplete/sqlSpecSelect.js

@@ -136,9 +136,10 @@ define([
       assertAutoComplete({
         beforeCursor: 'SELECT bar FROM foo WHERE id = 1 ',
         afterCursor: '',
+        dialect: 'generic',
         expectedResult: {
           lowerCase: false,
-          suggestKeywords: ['<', '<=', '<>', '=', '>', '>=', 'AND', 'GROUP BY', 'IN', 'LIMIT', 'NOT IN', 'OR', 'ORDER BY']
+          suggestKeywords: ['<', '<=', '<>', '=', '>', '>=', 'AND', 'BETWEEN', 'GROUP BY', 'IN', 'LIMIT', 'NOT BETWEEN', 'NOT IN', 'OR', 'ORDER BY']
         }
       });
     });
@@ -147,9 +148,10 @@ define([
       assertAutoComplete({
         beforeCursor: 'SELECT * FROM foo WHERE id = 1 ',
         afterCursor: '',
+        dialect: 'generic',
         expectedResult: {
           lowerCase: false,
-          suggestKeywords: ['<', '<=', '<>', '=', '>', '>=', 'AND', 'GROUP BY', 'IN', 'LIMIT', 'NOT IN', 'OR', 'ORDER BY']
+          suggestKeywords: ['<', '<=', '<>', '=', '>', '>=', 'AND', 'BETWEEN', 'GROUP BY', 'IN', 'LIMIT', 'NOT BETWEEN', 'NOT IN', 'OR', 'ORDER BY']
         }
       });
     });
@@ -222,7 +224,7 @@ define([
           dialect: 'hive',
           expectedResult: {
             lowerCase: false,
-            suggestKeywords: ['<', '<=', '<>', '=', '>', '>=', 'AND', 'GROUP BY', 'IN', 'LIMIT', 'NOT IN', 'OR', 'ORDER BY']
+            suggestKeywords: ['<', '<=', '<=>', '<>', '=', '>', '>=', 'AND', 'BETWEEN', 'GROUP BY', 'IN', 'LIMIT', 'NOT BETWEEN', 'NOT IN', 'OR', 'ORDER BY']
           }
         });
       });
@@ -1501,6 +1503,84 @@ define([
         });
       });
 
+      it('should suggest keywords for between after value expression', function() {
+        assertAutoComplete({
+          beforeCursor: 'SELECT * FROM testTable WHERE a ',
+          afterCursor: '',
+          containsKeywords: ['BETWEEN', 'NOT BETWEEN'],
+          expectedResult: {
+            lowerCase: false
+          }
+        });
+      });
+
+      it('should suggest keywords for like and regex after value expression', function() {
+        assertAutoComplete({
+          beforeCursor: 'SELECT * FROM testTable WHERE a ',
+          afterCursor: '',
+          containsKeywords: ['LIKE', 'RLIKE', 'REGEX', 'NOT LIKE'],
+          expectedResult: {
+            lowerCase: false
+          }
+        });
+      });
+
+      it('should suggest keywords for between after value expression', function() {
+        assertAutoComplete({
+          beforeCursor: 'SELECT * FROM testTable WHERE a NOT ',
+          afterCursor: '',
+          expectedResult: {
+            lowerCase: false,
+            suggestKeywords: ['BETWEEN', 'EXISTS', 'IN', 'LIKE']
+          }
+        });
+      });
+
+      it('should suggest values after between', function() {
+        assertAutoComplete({
+          beforeCursor: 'SELECT * FROM testTable WHERE a BETWEEN ',
+          afterCursor: '',
+          expectedResult: {
+            lowerCase: false,
+            suggestValues: { table: 'testTable', identifierChain: [{ name: 'a' }] },
+            suggestColumns: { table: 'testTable' }
+          }
+        });
+      });
+
+      it('should suggest suggest select for exists subquery', function() {
+        assertAutoComplete({
+          beforeCursor: 'SELECT * FROM testTable WHERE a NOT EXISTS (',
+          afterCursor: '',
+          expectedResult: {
+            lowerCase: false,
+            suggestKeywords: ['SELECT']
+          }
+        });
+      });
+
+      it('should suggest keywords after IS', function() {
+        assertAutoComplete({
+          beforeCursor: 'SELECT * FROM testTable WHERE a IS ',
+          afterCursor: '',
+          expectedResult: {
+            lowerCase: false,
+            suggestKeywords: ['FALSE', 'NOT FALSE', 'NOT NULL', 'NOT TRUE', 'NULL', 'TRUE']
+          }
+        });
+      });
+
+      it('should suggest keywords after IS and before TRUE', function() {
+        assertAutoComplete({
+          beforeCursor: 'SELECT * FROM testTable WHERE a IS ',
+          afterCursor: ' TRUE',
+          expectedResult: {
+            lowerCase: false,
+            suggestKeywords: ['NOT']
+          }
+        });
+      });
+
       describe('ORDER BY Clause', function () {
         it('should handle complete ORDER BY', function () {
           assertAutoComplete({
@@ -2233,9 +2313,10 @@ define([
         assertAutoComplete({
           beforeCursor: 'SELECT * FROM foo WHERE bar ',
           afterCursor: '',
+          dialect: 'generic',
+          containsKeywords: ['IN', 'NOT IN'],
           expectedResult: {
-            lowerCase: false,
-            suggestKeywords: ['<', '<=', '<>', '=', '>', '>=', 'AND', 'GROUP BY', 'IN', 'LIMIT', 'NOT IN', 'OR', 'ORDER BY']
+            lowerCase: false
           }
         });
       });
@@ -2244,9 +2325,9 @@ define([
         assertAutoComplete({
           beforeCursor: 'SELECT * FROM foo WHERE bar NOT ',
           afterCursor: '',
+          containsKeywords: ['IN'],
           expectedResult: {
-            lowerCase: false,
-            suggestKeywords: ['IN']
+            lowerCase: false
           }
         });
       });

Some files were not shown because too many files changed in this diff