You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by zj...@apache.org on 2018/08/10 01:51:18 UTC

zeppelin git commit: [ZEPPELIN-3677] SAP Interpreter improvement

Repository: zeppelin
Updated Branches:
  refs/heads/master f699b3c2d -> 6455e2aca


[ZEPPELIN-3677] SAP Interpreter improvement

### What is this PR for?
Improvements:
- An empty result processing, message about no data will be shown;
- Dimension's attributes auto complete and processing;
- Property `universe.queryTimeout` for socket and connection timeouts;
- Interpolation support and property for it;
- `completionSupport` flag fix;
- Support of `distinct` keyword (written after `select`);
- Support of `limit %d` keyword (written in the end of query);
- Add concurrency support, relevant properties are `universe.concurrent.use` and `universe.concurrent.maxConnection`;
- `;` is adding to the end of query if it has not it.

### What type of PR is it?
Improvement

### What is the Jira issue?
[ZEPPELIN-3677](https://issues.apache.org/jira/projects/ZEPPELIN/issues/ZEPPELIN-3677?filter=allopenissues)

### Questions:
* Does the licenses files need update? No
* Is there breaking changes for older versions? No
* Does this need documentation? Updated

Author: oxygen311 <al...@mail.ru>

Closes #3106 from oxygen311/DW-17701 and squashes the following commits:

480e6487e [oxygen311] Add default values to properties
faf85a76d [oxygen311] Docs fix
5668ad376 [oxygen311] Add distinct support if only one object in select block
7e4e4a5e0 [oxygen311] Add `distinct` and `limit` keywords support
bfc555682 [oxygen311] Add concurrent mode to universe interpreter
d5edcd44e [oxygen311] Add Object Interpolation
832d38cab [oxygen311] One property against two
48bb5ee01 [oxygen311] Add properties to `interpreter-setting.json`
f15b95942 [oxygen311] Shows message if result is empty
4c29d104c [oxygen311] Add `universe.connectTimeout` and `universe.socketTimeout` properties
443d0afaf [oxygen311] Att attributes to auto complete
86c978959 [oxygen311] Add attribute info to `UniverseNodeInfo` list
d28b8217f [oxygen311] Change flag in interpreter settings


Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/6455e2ac
Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/6455e2ac
Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/6455e2ac

Branch: refs/heads/master
Commit: 6455e2aca4f6a8b92661d0a95105327832ee5644
Parents: f699b3c
Author: oxygen311 <al...@mail.ru>
Authored: Wed Aug 8 13:49:41 2018 +0200
Committer: Jeff Zhang <zj...@apache.org>
Committed: Fri Aug 10 09:51:10 2018 +0800

----------------------------------------------------------------------
 docs/interpreter/sap.md                         | 18 +++++
 .../zeppelin/sap/UniverseInterpreter.java       | 37 ++++++++-
 .../zeppelin/sap/universe/UniverseClient.java   | 84 +++++++++++++++++---
 .../sap/universe/UniverseNodeInfoCompleter.java | 20 +++--
 .../zeppelin/sap/universe/UniverseQuery.java    | 17 +++-
 .../zeppelin/sap/universe/UniverseUtil.java     | 49 ++++++++++--
 sap/src/main/resources/interpreter-setting.json | 30 ++++++-
 7 files changed, 226 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6455e2ac/docs/interpreter/sap.md
----------------------------------------------------------------------
diff --git a/docs/interpreter/sap.md b/docs/interpreter/sap.md
index 0447958..2c4128c 100644
--- a/docs/interpreter/sap.md
+++ b/docs/interpreter/sap.md
@@ -121,3 +121,21 @@ where
 
   and [Folder1].[Dimension5] in ('Value1', 'Value2');
 ```
+
+## Object Interpolation
+The SAP interpreter also supports interpolation of `ZeppelinContext` objects into the paragraph text.
+To enable this feature set `universe.interpolation` to `true`. The following example shows one use of this facility:
+
+####In Scala cell:
+
+```scala
+z.put("curr_date", "2018-01-01 00:00:00")
+```
+
+####In later SAP cell:
+
+```sql
+where
+   [Filter1]
+   and [Date] > '{curr_date}'
+```
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6455e2ac/sap/src/main/java/org/apache/zeppelin/sap/UniverseInterpreter.java
----------------------------------------------------------------------
diff --git a/sap/src/main/java/org/apache/zeppelin/sap/UniverseInterpreter.java b/sap/src/main/java/org/apache/zeppelin/sap/UniverseInterpreter.java
index 1fdb6d6..17da1c9 100644
--- a/sap/src/main/java/org/apache/zeppelin/sap/UniverseInterpreter.java
+++ b/sap/src/main/java/org/apache/zeppelin/sap/UniverseInterpreter.java
@@ -24,6 +24,8 @@ import org.apache.zeppelin.interpreter.InterpreterException;
 import org.apache.zeppelin.interpreter.InterpreterResult;
 import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
 import org.apache.zeppelin.sap.universe.*;
+import org.apache.zeppelin.scheduler.Scheduler;
+import org.apache.zeppelin.scheduler.SchedulerFactory;
 
 
 import java.util.*;
@@ -49,6 +51,11 @@ public class UniverseInterpreter extends Interpreter {
   private static final char NEWLINE = '\n';
   private static final char TAB = '\t';
   private static final String TABLE_MAGIC_TAG = "%table ";
+  private static final String EMPTY_DATA_MESSAGE = "%html\n" +
+      "<h4><center><b>No Data Available</b></center></h4>";
+
+  private static final String CONCURRENT_EXECUTION_KEY = "universe.concurrent.use";
+  private static final String CONCURRENT_EXECUTION_COUNT = "universe.concurrent.maxConnection";
 
   @Override
   public void open() throws InterpreterException {
@@ -56,7 +63,10 @@ public class UniverseInterpreter extends Interpreter {
     String password = getProperty("universe.password");
     String apiUrl = getProperty("universe.api.url");
     String authType = getProperty("universe.authType");
-    this.client = new UniverseClient(user, password, apiUrl, authType);
+    final int queryTimeout = Integer.parseInt(
+        StringUtils.defaultIfEmpty(getProperty("universe.queryTimeout"), "7200000"));
+    this.client =
+        new UniverseClient(user, password, apiUrl, authType, queryTimeout);
     this.universeUtil = new UniverseUtil();
   }
 
@@ -70,8 +80,10 @@ public class UniverseInterpreter extends Interpreter {
   }
 
   @Override
-  public InterpreterResult interpret(String st, InterpreterContext context)
+  public InterpreterResult interpret(String originalSt, InterpreterContext context)
       throws InterpreterException {
+    final String st = Boolean.parseBoolean(getProperty("universe.interpolation", "false")) ?
+        interpolate(originalSt, context.getResourcePool()) : originalSt;
     try {
       InterpreterResult interpreterResult = new InterpreterResult(InterpreterResult.Code.SUCCESS);
       String paragraphId = context.getParagraphId();
@@ -153,9 +165,30 @@ public class UniverseInterpreter extends Interpreter {
     return candidates;
   }
 
+  @Override
+  public Scheduler getScheduler() {
+    String schedulerName = UniverseInterpreter.class.getName() + this.hashCode();
+    return isConcurrentExecution() ?
+        SchedulerFactory.singleton().createOrGetParallelScheduler(schedulerName,
+            getMaxConcurrentConnection())
+        : SchedulerFactory.singleton().createOrGetFIFOScheduler(schedulerName);
+  }
+
+  private boolean isConcurrentExecution() {
+    return Boolean.valueOf(getProperty(CONCURRENT_EXECUTION_KEY, "true"));
+  }
+
+  private int getMaxConcurrentConnection() {
+    return Integer.valueOf(
+        StringUtils.defaultIfEmpty(getProperty(CONCURRENT_EXECUTION_COUNT), "10"));
+  }
+
   private String formatResults(List<List<String>> results) {
     StringBuilder msg = new StringBuilder();
     if (results != null) {
+      if (results.isEmpty()) {
+        return EMPTY_DATA_MESSAGE;
+      }
       msg.append(TABLE_MAGIC_TAG);
       for (int i = 0; i < results.size(); i++) {
         List<String> items = results.get(i);

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6455e2ac/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseClient.java
----------------------------------------------------------------------
diff --git a/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseClient.java b/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseClient.java
index b60eac4..c397f45 100644
--- a/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseClient.java
+++ b/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseClient.java
@@ -22,7 +22,6 @@ import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.exception.ExceptionUtils;
 import org.apache.http.Header;
 import org.apache.http.HttpResponse;
-import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.config.RequestConfig;
 import org.apache.http.client.methods.*;
 import org.apache.http.entity.StringEntity;
@@ -97,11 +96,15 @@ public class UniverseClient {
       "<query xmlns=\"http://www.sap.com/rws/sl/universe\" dataSourceType=\"%s\" " +
           "dataSourceId=\"%s\">\n" +
           "<querySpecification version=\"1.0\">\n" +
+          "   <queryOptions>\n" +
+          "            <queryOption name=\"duplicatedRows\" value=\"%s\"/>\n" +
+          "            <queryOption name=\"maxRowsRetrieved\" activated=\"%s\" value=\"%d\"/>\n" +
+          "  </queryOptions>" +
           "  <queryData>\n%s\n" +
           "     %s\n" +
-          "</queryData>\n" +
+          "  </queryData>\n" +
           "</querySpecification>\n" +
-      "</query>\n";
+          "</query>\n";
   private final String filterPartTemplate = "<filterPart>%s\n</filterPart>";
   private final String errorMessageTemplate = "%s\n\n%s";
   private final String parameterTemplate = "<parameter type=\"prompt\">\n" +
@@ -117,10 +120,11 @@ public class UniverseClient {
       "              </values>\n" +
       "        </answer>\n";
 
-  public UniverseClient(String user, String password, String apiUrl, String authType) {
+  public UniverseClient(String user, String password, String apiUrl, String authType,
+                        int queryTimeout) {
     RequestConfig requestConfig = RequestConfig.custom()
-        .setConnectTimeout(20 * 60 * 1000)
-        .setSocketTimeout(20 * 60 * 1000)
+        .setConnectTimeout(queryTimeout)
+        .setSocketTimeout(queryTimeout)
         .build();
     PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
     cm.setMaxTotal(100);
@@ -160,7 +164,9 @@ public class UniverseClient {
           String.format(filterPartTemplate, query.getWhere()) : StringUtils.EMPTY;
       httpPost.setEntity(new StringEntity(
           String.format(createQueryRequestTemplate, query.getUniverseInfo().getType(),
-              query.getUniverseInfo().getId(), query.getSelect(), where), "UTF-8"));
+              query.getUniverseInfo().getId(), query.getDuplicatedRows(),
+              query.getMaxRowsRetrieved().isPresent(), query.getMaxRowsRetrieved().orElse(0),
+              query.getSelect(), where), "UTF-8"));
       HttpResponse response = httpClient.execute(httpPost);
 
       if (response.getStatusLine().getStatusCode() == 200) {
@@ -320,7 +326,7 @@ public class UniverseClient {
             }
           } catch (Exception e) {
             throw new UniverseException(String.format(errorMessageTemplate, "UniverseClient "
-                + "(get universe nodes info): Response processing failed",
+                    + "(get universe nodes info): Response processing failed",
                 ExceptionUtils.getStackTrace(e)));
           }
         }
@@ -501,7 +507,7 @@ public class UniverseClient {
         }
       } catch (IOException e) {
         throw new UniverseException(String.format(errorMessageTemplate, "UniverseClient "
-                + "(get universes): Response processing failed", ExceptionUtils.getStackTrace(e)));
+            + "(get universes): Response processing failed", ExceptionUtils.getStackTrace(e)));
       } catch (ParserConfigurationException | SAXException | XPathExpressionException e) {
         throw new UniverseException(String.format(errorMessageTemplate, "UniverseClient "
             + "(get universes): Response processing failed", ExceptionUtils.getStackTrace(e)));
@@ -649,6 +655,63 @@ public class UniverseClient {
     return results;
   }
 
+  private void addAttributesToDimension(Node universeNode, Map<String, UniverseNodeInfo> nodes) {
+    final NodeList attributeNodes = universeNode.getChildNodes();
+    final int attributeNodesCount = attributeNodes.getLength();
+
+    for (int i = 0; i < attributeNodesCount; ++i) {
+      final Node attributeNode = attributeNodes.item(i);
+
+      if (attributeNode.getNodeName().equalsIgnoreCase(EL_ITEM)) {
+        final NodeList childNodes = attributeNode.getChildNodes();
+        final int childNodesCount = childNodes.getLength();
+
+        String nodeId = null;
+        String nodeName = null;
+        String nodePath = null;
+        for (int j = 0; j < childNodesCount; j++) {
+          Node childNode = childNodes.item(j);
+          if (childNode.getNodeType() == Node.ELEMENT_NODE) {
+            switch (childNode.getNodeName().toLowerCase()) {
+              case EL_NAME:
+                nodeName = childNode.getTextContent();
+                break;
+              case EL_ID:
+                nodeId = childNode.getTextContent();
+                break;
+              case EL_PATH:
+                nodePath = childNode.getTextContent();
+                break;
+            }
+          }
+        }
+        StringBuilder key = new StringBuilder();
+        if (StringUtils.isNotBlank(nodeName)) {
+          String nodeType = null;
+          String[] parts = nodePath.split("\\\\");
+          List<String> path = new ArrayList();
+          for (String part : parts) {
+            String[] p = part.split("\\|");
+            if (p.length == 2) {
+              if (p[1].equalsIgnoreCase("folder") || p[1].equalsIgnoreCase("dimension")) {
+                path.add(p[0]);
+              } else {
+                nodeName = p[0];
+                nodeType = p[1];
+              }
+            }
+          }
+          final String folder = StringUtils.join(path, "\\");
+          key.append("[");
+          key.append(StringUtils.join(path, "].["));
+          key.append(String.format("].[%s]", nodeName));
+          nodes.put(key.toString(),
+              new UniverseNodeInfo(nodeId, nodeName, nodeType, folder, nodePath));
+        }
+      }
+    }
+  }
+
   private void parseUniverseInfo(NodeList universeInfoNodes, Map<String, UniverseNodeInfo> nodes) {
     if (universeInfoNodes != null) {
       int count = universeInfoNodes.getLength();
@@ -710,6 +773,9 @@ public class UniverseClient {
                     } else {
                       nodeName = p[0];
                       nodeType = p[1];
+                      if (p[1].equalsIgnoreCase("dimension")) {
+                        addAttributesToDimension(node, nodes);
+                      }
                     }
                   }
                 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6455e2ac/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseNodeInfoCompleter.java
----------------------------------------------------------------------
diff --git a/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseNodeInfoCompleter.java b/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseNodeInfoCompleter.java
index 0704e1b..af46b46 100644
--- a/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseNodeInfoCompleter.java
+++ b/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseNodeInfoCompleter.java
@@ -161,23 +161,27 @@ public class UniverseNodeInfoCompleter implements Completer {
     }
 
     public void putInfo(UniverseNodeInfo info) {
-      children.put(info.getName(), info);
+      children.put(info.getId(), info);
     }
 
-    public List<UniverseNodeInfo> getNodesInfo() {
-      List<UniverseNodeInfo> list = new ArrayList<>();
+    public Collection<UniverseNodeInfo> getNodesInfo() {
+      HashMap<String, UniverseNodeInfo> map = new HashMap<>();
       if (children != null) {
         for (Object o : children.values()) {
           if (o instanceof UniverseNodeInfo) {
-            list.add((UniverseNodeInfo) o);
+            final UniverseNodeInfo nodeInfo = (UniverseNodeInfo) o;
+            map.put(nodeInfo.getName(), nodeInfo);
           } else {
-            UniverseInfoTreeNode treeNode = (UniverseInfoTreeNode) o;
-            list.add(new UniverseNodeInfo(treeNode.getName(), UniverseCompleter.TYPE_FOLDER));
+            final UniverseInfoTreeNode treeNode = (UniverseInfoTreeNode) o;
+            final UniverseNodeInfo nodeInfo =
+                new UniverseNodeInfo(treeNode.getName(), UniverseCompleter.TYPE_FOLDER);
+            if (!map.containsKey(nodeInfo.getName())) {
+              map.put(nodeInfo.getName(), nodeInfo);
+            }
           }
         }
       }
-
-      return list;
+      return map.values();
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6455e2ac/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseQuery.java
----------------------------------------------------------------------
diff --git a/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseQuery.java b/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseQuery.java
index 824d1be..43894b2 100644
--- a/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseQuery.java
+++ b/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseQuery.java
@@ -19,6 +19,8 @@ package org.apache.zeppelin.sap.universe;
 
 import org.apache.commons.lang.StringUtils;
 
+import java.util.OptionalInt;
+
 /**
  * Data of universe query
  */
@@ -26,11 +28,16 @@ public class UniverseQuery {
   private String select;
   private String where;
   private UniverseInfo universeInfo;
+  private boolean duplicatedRows = false;
+  private OptionalInt maxRowsRetrieved;
 
-  public UniverseQuery(String select, String where, UniverseInfo universeInfo) {
+  public UniverseQuery(String select, String where, UniverseInfo universeInfo,
+                       boolean duplicatedRows, OptionalInt maxRowsRetrieved) {
     this.select = select;
     this.where = where;
     this.universeInfo = universeInfo;
+    this.duplicatedRows = duplicatedRows;
+    this.maxRowsRetrieved = maxRowsRetrieved;
   }
 
   public boolean isCorrect() {
@@ -50,4 +57,12 @@ public class UniverseQuery {
   public UniverseInfo getUniverseInfo() {
     return universeInfo;
   }
+
+  public boolean getDuplicatedRows() {
+    return duplicatedRows;
+  }
+
+  public OptionalInt getMaxRowsRetrieved() {
+    return maxRowsRetrieved;
+  }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6455e2ac/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseUtil.java
----------------------------------------------------------------------
diff --git a/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseUtil.java b/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseUtil.java
index 31fbf51..6f24639 100644
--- a/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseUtil.java
+++ b/sap/src/main/java/org/apache/zeppelin/sap/universe/UniverseUtil.java
@@ -17,6 +17,7 @@
 
 package org.apache.zeppelin.sap.universe;
 
+import jline.console.completer.ArgumentCompleter.WhitespaceArgumentDelimiter;
 import org.apache.commons.lang.StringUtils;
 
 import java.util.*;
@@ -64,6 +65,7 @@ public class UniverseUtil {
   private static final String RIGHT_BRACE = ")";
 
   public static final Map<String, Integer> OPERATIONS;
+  private static final WhitespaceArgumentDelimiter delimiter = new WhitespaceArgumentDelimiter();
 
   static {
     OPERATIONS = new HashMap<>();
@@ -82,6 +84,14 @@ public class UniverseUtil {
     OPERATIONS.put(MARKER_OR, 3);
   }
 
+  public static OptionalInt parseInt(String toParse) {
+    try {
+      return OptionalInt.of(Integer.parseInt(toParse));
+    } catch (NumberFormatException e) {
+      return OptionalInt.empty();
+    }
+  }
+
   public UniverseQuery convertQuery(String text, UniverseClient client, String token)
       throws UniverseException {
     StringBuilder select = new StringBuilder();
@@ -98,7 +108,27 @@ public class UniverseUtil {
     boolean wherePart = false;
     boolean listOperator = false;
     boolean operatorPosition = false;
+    boolean duplicatedRows = true;
     Map<String, UniverseNodeInfo> nodeInfos = null;
+    OptionalInt limit = OptionalInt.empty();
+
+    final int limitIndex = text.lastIndexOf("limit");
+    if (limitIndex != -1) {
+      final String[] arguments = delimiter.delimit(text, 0).getArguments();
+      final int length = arguments.length;
+      if (arguments[length - 3].equals("limit")) {
+        limit = parseInt(arguments[length - 2]);
+      } else if (arguments[length - 2].equals("limit")) {
+        final String toParse = arguments[length - 1];
+        limit = parseInt(toParse.endsWith(";") ?
+            toParse.substring(0, toParse.length() - 1) : toParse);
+      }
+      text = text.substring(0, limitIndex);
+    }
+
+    if (!text.endsWith(";")) {
+      text += ";";
+    }
 
     char[] array = text.toCharArray();
     for (int i = 0; i < array.length; i++) {
@@ -188,7 +218,8 @@ public class UniverseUtil {
         }
         if (buf.toString().toLowerCase().endsWith("where") || i == array.length - 1) {
           selectPart = false;
-          select.append(parseResultObj(resultObj.toString().replaceAll("(?i)wher$", ""),
+          select.append(parseResultObj(resultObj.toString()
+                  .replaceAll("(?i)wher$", "").replaceAll("(?i)distinc", ""),
               nodeInfos));
           select.append(RESULT_END_TEMPLATE);
           continue;
@@ -196,8 +227,12 @@ public class UniverseUtil {
       }
 
       if (selectPart) {
+        if (pathClosed && singleQuoteClosed && buf.toString().toLowerCase().endsWith("distinct")) {
+          duplicatedRows = false;
+          continue;
+        }
         if (pathClosed && singleQuoteClosed && c == ',') {
-          select.append(parseResultObj(resultObj.toString(), nodeInfos));
+          select.append(parseResultObj(resultObj.toString().replaceAll("(?i)distinc", ""), nodeInfos));
           resultObj = new StringBuilder();
         } else {
           resultObj.append(c);
@@ -206,7 +241,7 @@ public class UniverseUtil {
       }
 
       if (wherePart) {
-        if (c == ';' && pathClosed && singleQuoteClosed) {
+        if (c == ';' && pathClosed && singleQuoteClosed) { // todo: check for limit
           wherePart = false;
           where = parseWhere(whereBuf.toString(), nodeInfos);
         } else {
@@ -362,7 +397,7 @@ public class UniverseUtil {
     }
 
     UniverseQuery universeQuery = new UniverseQuery(select.toString().trim(),
-        where, universeInfo);
+        where, universeInfo, duplicatedRows, limit);
 
     if (!universeQuery.isCorrect()) {
       throw new UniverseException("Incorrect query");
@@ -403,8 +438,7 @@ public class UniverseUtil {
         }
         if (nextOperation.equals(LEFT_BRACE)) {
           stack.push(nextOperation);
-        }
-        else if (nextOperation.equals(RIGHT_BRACE)) {
+        } else if (nextOperation.equals(RIGHT_BRACE)) {
           while (!stack.peek().equals(LEFT_BRACE)) {
             out.add(stack.pop());
             if (stack.empty()) {
@@ -412,8 +446,7 @@ public class UniverseUtil {
             }
           }
           stack.pop();
-        }
-        else {
+        } else {
           while (!stack.empty() && !stack.peek().equals(LEFT_BRACE) &&
               (OPERATIONS.get(nextOperation) >= OPERATIONS.get(stack.peek()))) {
             out.add(stack.pop());

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/6455e2ac/sap/src/main/resources/interpreter-setting.json
----------------------------------------------------------------------
diff --git a/sap/src/main/resources/interpreter-setting.json b/sap/src/main/resources/interpreter-setting.json
index 9ab78b8..548d545 100644
--- a/sap/src/main/resources/interpreter-setting.json
+++ b/sap/src/main/resources/interpreter-setting.json
@@ -32,12 +32,40 @@
         "defaultValue": "secEnterprise",
         "description": "Type of authentication for API of Universe. Available values: secEnterprise, secLDAP, secWinAD, secSAPR3",
         "type": "string"
+      },
+      "universe.queryTimeout": {
+        "envName": null,
+        "propertyName": "universe.queryTimeout",
+        "defaultValue": "7200000",
+        "description": "Query timeout for API of Universe",
+        "type": "number"
+      },
+      "universe.interpolation": {
+        "envName": null,
+        "propertyName": "universe.interpolation",
+        "defaultValue": false,
+        "description": "Enable ZeppelinContext variable interpolation into paragraph text",
+        "type": "checkbox"
+      },
+      "universe.concurrent.use": {
+        "envName": null,
+        "propertyName": "universe.concurrent.use",
+        "defaultValue": true,
+        "description": "Use parallel scheduler",
+        "type": "checkbox"
+      },
+      "universe.concurrent.maxConnection": {
+        "envName": null,
+        "propertyName": "universe.concurrent.maxConnection",
+        "defaultValue": "10",
+        "description": "Number of concurrent execution",
+        "type": "number"
       }
     },
     "editor": {
       "editOnDblClick": false,
       "completionKey": "TAB",
-      "completionSupport": true
+      "completionSupport": false
     }
   }
 ]