Browse Source

HUE-7913 [autocomplete] Pair variable locations with columns where possible

Johan Ahlen 7 years ago
parent
commit
5c77f46

+ 20 - 4
desktop/core/src/desktop/static/desktop/js/autocomplete/jison/sql_valueExpression.jison

@@ -140,10 +140,26 @@ ValueExpression_EDIT
 // ------------------  COMPARISON ------------------
 
 ValueExpression
- : ValueExpression '=' ValueExpression                    -> { types: [ 'BOOLEAN' ] }
- | ValueExpression '<' ValueExpression  -> { types: [ 'BOOLEAN' ] }
- | ValueExpression '>' ValueExpression  -> { types: [ 'BOOLEAN' ] }
- | ValueExpression 'COMPARISON_OPERATOR' ValueExpression  -> { types: [ 'BOOLEAN' ] }
+ : ValueExpression '=' ValueExpression
+   {
+     parser.addColRefToVariableIfExists($1, $3);
+     $$ = { types: [ 'BOOLEAN' ] };
+   }
+ | ValueExpression '<' ValueExpression
+   {
+     parser.addColRefToVariableIfExists($1, $3);
+     $$ = { types: [ 'BOOLEAN' ] };
+   }
+ | ValueExpression '>' ValueExpression
+   {
+     parser.addColRefToVariableIfExists($1, $3);
+     $$ = { types: [ 'BOOLEAN' ] };
+   }
+ | ValueExpression 'COMPARISON_OPERATOR' ValueExpression
+   {
+     parser.addColRefToVariableIfExists($1, $3);
+     $$ = { types: [ 'BOOLEAN' ] };
+   }
  ;
 
 ValueExpression_EDIT

+ 78 - 17
desktop/core/src/desktop/static/desktop/js/autocomplete/spec/sqlSpecLocations.js

@@ -177,22 +177,6 @@
       });
     });
 
-    it('should report locations for "select * from tbl where col = ${var_name=10}; |"', function() {
-      assertLocations({
-        beforeCursor: 'select * from tbl where col = ${var_name=10}; ',
-        expectedLocations: [
-          { type: 'statement', location: { first_line: 1, last_line: 1, first_column: 1, last_column: 45 } },
-          { type: 'selectList', missing: false, location: { first_line: 1, last_line: 1, first_column: 8, last_column: 9 } },
-          { type: 'asterisk', location: { first_line: 1, last_line: 1, first_column: 8, last_column: 9 }, tables: [{ identifierChain: [{ name: 'tbl' }] }] },
-          { type: 'table', location: { first_line: 1, last_line: 1, first_column: 15, last_column: 18 }, identifierChain: [{ name: 'tbl' }] },
-          { type: 'whereClause', missing: false, location: { first_line: 1, last_line: 1, first_column: 19, last_column: 45 } },
-          { type: 'column', location: { first_line: 1, last_line: 1, first_column: 25, last_column: 28 }, identifierChain: [{ name: 'col' }], tables: [{ identifierChain: [{ name: 'tbl' }] }], qualified: false },
-          { type: 'variable', location: { first_line: 1, last_line: 1, first_column: 31, last_column: 45 }, value: '${var_name=10}' },
-          { type: 'limitClause', missing: true, location: { first_line: 1, last_line: 1, first_column: 45, last_column: 45 } }
-        ]
-      });
-    });
-
     it('should report locations for "SELECT * FROM testTable1 JOIN db1.table2; |"', function() {
       assertLocations({
         beforeCursor: 'SELECT * FROM testTable1 JOIN db1.table2; ',
@@ -970,6 +954,23 @@
       });
 
       describe('variable references', function () {
+
+        it('should variable location for "select * from tbl where col = ${var_name=10}; |"', function() {
+          assertLocations({
+            beforeCursor: 'select * from tbl where col = ${var_name=10}; ',
+            expectedLocations: [
+              { type: 'statement', location: { first_line: 1, last_line: 1, first_column: 1, last_column: 45 } },
+              { type: 'selectList', missing: false, location: { first_line: 1, last_line: 1, first_column: 8, last_column: 9 } },
+              { type: 'asterisk', location: { first_line: 1, last_line: 1, first_column: 8, last_column: 9 }, tables: [{ identifierChain: [{ name: 'tbl' }] }] },
+              { type: 'table', location: { first_line: 1, last_line: 1, first_column: 15, last_column: 18 }, identifierChain: [{ name: 'tbl' }] },
+              { type: 'whereClause', missing: false, location: { first_line: 1, last_line: 1, first_column: 19, last_column: 45 } },
+              { type: 'column', location: { first_line: 1, last_line: 1, first_column: 25, last_column: 28 }, identifierChain: [{ name: 'col' }], tables: [{ identifierChain: [{ name: 'tbl' }] }], qualified: false },
+              { type: 'variable', location: { first_line: 1, last_line: 1, first_column: 31, last_column: 45 }, value: '${var_name=10}', colRef: { identifierChain: [{ name: 'col' }], tables: [{ identifierChain: [{ name: 'tbl' }] }] } },
+              { type: 'limitClause', missing: true, location: { first_line: 1, last_line: 1, first_column: 45, last_column: 45 } }
+            ]
+          });
+        });
+
         it('should report variable location for "SELECT * FROM testTable WHERE foo = ${some_var}; |"', function () {
           assertLocations({
             dialect: 'impala',
@@ -982,11 +983,71 @@
               { type: 'table', location: { first_line: 1, last_line: 1, first_column: 15, last_column: 24 }, identifierChain: [{ name: 'testTable' }] },
               { type: 'whereClause', missing: false, location: { first_line: 1, last_line: 1, first_column: 25, last_column: 48 } },
               { type: 'column', location: { first_line: 1, last_line: 1, first_column: 31, last_column: 34 }, identifierChain: [{ name: 'foo' }], qualified: false, tables: [{ identifierChain: [{ name: 'testTable' }] }] },
-              { type: 'variable', location: { first_line: 1, last_line: 1, first_column: 37, last_column: 48 }, value: '${some_var}' },
+              { type: 'variable', location: { first_line: 1, last_line: 1, first_column: 37, last_column: 48 }, value: '${some_var}', colRef: { identifierChain: [{ name: 'foo' }], tables: [{ identifierChain: [{ name: 'testTable' }] }] } },
               { type: 'limitClause', missing: true, location: { first_line: 1, last_line: 1, first_column: 48, last_column: 48 } }
             ]
           });
         });
+
+        it('should report variable location for "SELECT * FROM tbl1, tbl2 WHERE foo = ${some_var}; |"', function () {
+          assertLocations({
+            dialect: 'impala',
+            beforeCursor: 'SELECT * FROM tbl1, tbl2 WHERE foo = ${some_var}; ',
+            afterCursor: '',
+            expectedLocations: [
+              { type: 'statement', location: { first_line: 1, last_line: 1, first_column: 1, last_column: 49 } },
+              { type: 'selectList', missing: false, location: { first_line: 1, last_line: 1, first_column: 8, last_column: 9 } },
+              { type: 'asterisk', location: { first_line: 1, last_line: 1, first_column: 8, last_column: 9 }, tables: [{ identifierChain: [{ name: 'tbl1' }] }, { identifierChain: [{ name: 'tbl2' }] }] },
+              { type: 'table', location: { first_line: 1, last_line: 1, first_column: 15, last_column: 19 }, identifierChain: [{ name: 'tbl1' }] },
+              { type: 'table', location: { first_line: 1, last_line: 1, first_column: 21, last_column: 25 }, identifierChain: [{ name: 'tbl2' }] },
+              { type: 'whereClause', missing: false, location: { first_line: 1, last_line: 1, first_column: 26, last_column: 49 } },
+              { type: 'column', location: { first_line: 1, last_line: 1, first_column: 32, last_column: 35 }, identifierChain: [{ name: 'foo' }], qualified: false, tables: [{ identifierChain: [{ name: 'tbl1' }] }, { identifierChain: [{ name: 'tbl2' }] }] },
+              { type: 'variable', location: { first_line: 1, last_line: 1, first_column: 38, last_column: 49 }, value: '${some_var}', colRef: { identifierChain: [{ name: 'foo' }], tables: [{ identifierChain: [{ name: 'tbl1' }] }, { identifierChain: [{ name: 'tbl2' }] }] } },
+              { type: 'limitClause', missing: true, location: { first_line: 1, last_line: 1, first_column: 49, last_column: 49 } }
+            ]
+          });
+        });
+
+        it('should report variable location for "SELECT * FROM tbl1, tbl2 WHERE tbl1.foo = ${some_var}; |"', function () {
+          assertLocations({
+            dialect: 'impala',
+            beforeCursor: 'SELECT * FROM tbl1, tbl2 WHERE tbl1.foo = ${some_var}; ',
+            afterCursor: '',
+            expectedLocations: [
+              { type: 'statement', location: { first_line: 1, last_line: 1, first_column: 1, last_column: 54 } },
+              { type: 'selectList', missing: false, location: { first_line: 1, last_line: 1, first_column: 8, last_column: 9 } },
+              { type: 'asterisk', location: { first_line: 1, last_line: 1, first_column: 8, last_column: 9 }, tables: [{ identifierChain: [{ name: 'tbl1' }] }, { identifierChain: [{ name: 'tbl2' }] }] },
+              { type: 'table', location: { first_line: 1, last_line: 1, first_column: 15, last_column: 19 }, identifierChain: [{ name: 'tbl1' }] },
+              { type: 'table', location: { first_line: 1, last_line: 1, first_column: 21, last_column: 25 }, identifierChain: [{ name: 'tbl2' }] },
+              { type: 'whereClause', missing: false, location: { first_line: 1, last_line: 1, first_column: 26, last_column: 54 } },
+              { type: 'table', location: { first_line: 1, last_line: 1, first_column: 32, last_column: 36 }, identifierChain: [{ name: 'tbl1' }] },
+              { type: 'column', location: { first_line: 1, last_line: 1, first_column: 37, last_column: 40 }, identifierChain: [{ name: 'foo' }], qualified: true, tables: [{ identifierChain: [{ name: 'tbl1' }] }] },
+              { type: 'variable', location: { first_line: 1, last_line: 1, first_column: 43, last_column: 54 }, value: '${some_var}', colRef: { identifierChain: [{ name: 'foo' }], tables: [{ identifierChain: [{ name: 'tbl1' }] }] } },
+              { type: 'limitClause', missing: true, location: { first_line: 1, last_line: 1, first_column: 54, last_column: 54 } }
+            ]
+          });
+        });
+
+        it('should report variable location for "SELECT * FROM tbl1, somedb.tbl2 WHERE ${some_var} > foo.bar; |"', function () {
+          assertLocations({
+            dialect: 'impala',
+            beforeCursor: 'SELECT * FROM tbl1, somedb.tbl2 WHERE ${some_var} > foo.bar; ',
+            afterCursor: '',
+            expectedLocations: [
+              { type: 'statement', location: { first_line: 1, last_line: 1, first_column: 1, last_column: 60 } },
+              { type: 'selectList', missing: false, location: { first_line: 1, last_line: 1, first_column: 8, last_column: 9 } },
+              { type: 'asterisk', location: { first_line: 1, last_line: 1, first_column: 8, last_column: 9 }, tables: [{ identifierChain: [{ name: 'tbl1' }] }, { identifierChain: [{ name: 'somedb' }, { name: 'tbl2' }] }] },
+              { type: 'table', location: { first_line: 1, last_line: 1, first_column: 15, last_column: 19 }, identifierChain: [{ name: 'tbl1' }] },
+              { type: 'database', location: { first_line: 1, last_line: 1, first_column: 21, last_column: 27 }, identifierChain: [{ name: 'somedb' }] },
+              { type: 'table', location: { first_line: 1, last_line: 1, first_column: 28, last_column: 32 }, identifierChain: [{ name: 'somedb' }, { name: 'tbl2' }] },
+              { type: 'whereClause', missing: false, location: { first_line: 1, last_line: 1, first_column: 33, last_column: 60 } },
+              { type: 'variable', location: { first_line: 1, last_line: 1, first_column: 39, last_column: 50 }, value: '${some_var}', colRef: { identifierChain: [{ name: 'foo' }, { name: 'bar' }], tables: [{ identifierChain: [{ name: 'tbl1' }] }, { identifierChain: [{ name: 'somedb' }, { name: 'tbl2' }] }] } },
+              { type: 'column', location: { first_line: 1, last_line: 1, first_column: 53, last_column: 56 }, identifierChain: [{ name: 'foo' }], qualified: false, tables: [{ identifierChain: [{ name: 'tbl1' }] }, { identifierChain: [{ name: 'somedb' }, { name: 'tbl2' }] }] },
+              { type: 'complex', location: { first_line: 1, last_line: 1, first_column: 57, last_column: 60 }, identifierChain: [{ name: 'foo' }, { name: 'bar' }], qualified: true, tables: [{ identifierChain: [{ name: 'tbl1' }] }, { identifierChain: [{ name: 'somedb' }, { name: 'tbl2' }] }] },
+              { type: 'limitClause', missing: true, location: { first_line: 1, last_line: 1, first_column: 60, last_column: 60 } }
+            ]
+          });
+        });
       });
     })
   });

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


+ 27 - 0
desktop/core/src/desktop/static/desktop/js/autocomplete/sqlParseSupport.js

@@ -431,6 +431,10 @@ var SqlParseSupport = (function () {
 
       while (i--) {
         var location = parser.yy.locations[i];
+        if (location.type === 'variable' && location.colRef) {
+          parser.expandIdentifierChain({ wrapper: location.colRef, tablePrimaries: tablePrimaries, isColumnWrapper: true });
+          delete location.colRef.linked;
+        }
 
         // Impala can have references to previous tables after FROM, i.e. FROM testTable t, t.testArray
         // In this testArray would be marked a type table so we need to switch it to column.
@@ -1572,6 +1576,29 @@ var SqlParseSupport = (function () {
       return loc;
     };
 
+    parser.addColRefToVariableIfExists = function (left, right) {
+      if (left && left.columnReference && left.columnReference.length && right && right.columnReference && right.columnReference.length && parser.yy.locations.length > 1) {
+
+        var addColRefToVariableLocation = function (variableValue, colRef) {
+          for (var i = parser.yy.locations.length - 1; i > 0; i--) {
+            var location = parser.yy.locations[i];
+            if (location.type === 'variable' && location.value === variableValue) {
+              location.colRef = { identifierChain: colRef };
+              break;
+            }
+          }
+        };
+
+        if (/\$\{[^}]*\}/.test(left.columnReference[0].name)) {
+          // left is variable
+          addColRefToVariableLocation(left.columnReference[0].name, right.columnReference);
+        } else if (/\$\{[^}]*\}/.test(right.columnReference[0].name)) {
+          // right is variable
+          addColRefToVariableLocation(right.columnReference[0].name, left.columnReference);
+        }
+      }
+    };
+
     parser.suggestDatabases = function (details) {
       parser.yy.result.suggestDatabases = details || {};
     };

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


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