>From Janhavi Tripurwar <[email protected]>:

Janhavi Tripurwar has uploaded this change for review. ( 
https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/21689?usp=email )


Change subject: [ASTERIXDB-3593]: Return Explain results in response
......................................................................

[ASTERIXDB-3593]: Return Explain results in response

Ext-ref: MB-73758

Change-Id: I9673ff75c18d3113b51a9cf752986832a3be9e0b
---
M 
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/ExecutionPlans.java
M 
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
M 
asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/NcStatementsPrinter.java
M 
asterixdb/asterix-app/src/test/java/org/apache/asterix/app/result/NcStatementsPrinterTest.java
4 files changed, 89 insertions(+), 12 deletions(-)



  git pull ssh://asterix-gerrit.ics.uci.edu:29418/asterixdb 
refs/changes/89/21689/1

diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/ExecutionPlans.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/ExecutionPlans.java
index 5940efb..d97825e 100644
--- 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/ExecutionPlans.java
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/ExecutionPlans.java
@@ -32,6 +32,8 @@
     private String statementCategory;
     private String statementParameters;
     private boolean explainOnly;
+    /** The plan an EXPLAIN produced, which is reported as the statement's 
result rather than among its plans. */
+    private String explainResult;

     public ExecutionPlans() {
     }
@@ -47,6 +49,7 @@
         statementCategory = other.statementCategory;
         statementParameters = other.statementParameters;
         explainOnly = other.explainOnly;
+        explainResult = other.explainResult;
     }

     /** Forgets the plans of the statement that just finished, so the next one 
does not report them as its own. */
@@ -92,6 +95,7 @@
         statementCategory = null;
         statementParameters = null;
         explainOnly = false;
+        explainResult = null;
     }

     public String getExpressionTree() {
@@ -165,4 +169,12 @@
     public void setExplainOnly(boolean explainOnly) {
         this.explainOnly = explainOnly;
     }
+
+    public String getExplainResult() {
+        return explainResult;
+    }
+
+    public void setExplainResult(String explainResult) {
+        this.explainResult = explainResult;
+    }
 }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
index e0d6201..42c9450 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
@@ -440,6 +440,8 @@

             if (isExplainOnly) {
                 printPlanAsResult(metadataProvider, output, printer, 
printSignature);
+                // kept for a request reported statement by statement, where 
each statement prints its own result
+                
executionPlans.setExplainResult(executionPlans.getOptimizedLogicalPlan());
                 if (!conf.is(SessionConfig.OOB_OPTIMIZED_LOGICAL_PLAN)) {
                     executionPlans.setOptimizedLogicalPlan(null);
                 }
diff --git 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/NcStatementsPrinter.java
 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/NcStatementsPrinter.java
index 5b194a4..9dd4760 100644
--- 
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/NcStatementsPrinter.java
+++ 
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/NcStatementsPrinter.java
@@ -42,6 +42,7 @@
 import org.apache.asterix.translator.IStatementExecutor.ResultSetInfo;
 import org.apache.asterix.translator.IStatementExecutor.StatementInfo;
 import org.apache.asterix.translator.IStatementExecutor.Stats;
+import org.apache.asterix.translator.SessionConfig;
 import org.apache.asterix.translator.SessionOutput;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
 import org.apache.hyracks.api.exceptions.Warning;
@@ -134,15 +135,20 @@
         ResultReader rows = resultSetInfo != null && delivery == 
ResultDelivery.IMMEDIATE
                 ? new ResultReader(resultSet, resultSetInfo.getJobId(), 
resultSetInfo.getResultSetId()) : null;

+        // an EXPLAIN produces no result set; its plan is what the statement 
returns
+        String explainResult = plans == null ? null : plans.getExplainResult();
         fields.add(w -> printField(w, POSITION_FIELD_NAME, 
String.valueOf(statement.getPosition())));
         fields.add(w -> printField(w, KIND_FIELD_NAME, 
quoted(statementKind(statement))));
         // a statement that returns rows describes them, as the flat response 
does; the signature is the default one
         // unless the client asked for a typed one, exactly as 
SignaturePrinter.newInstance decides for a request
-        if (printSignature && resultSetInfo != null) {
+        if (printSignature && (resultSetInfo != null || explainResult != 
null)) {
             IResponseFieldPrinter signature =
                     plans == null ? SignaturePrinter.INSTANCE : 
SignaturePrinter.newInstance(plans);
             fields.add(w -> printNested(w, signature));
         }
+        if (explainResult != null) {
+            fields.add(w -> printExplain(w, explainResult));
+        }
         if (resultSetInfo != null) {
             if (delivery == ResultDelivery.IMMEDIATE) {
                 fields.add(w -> printRows(w, rows, resultSetInfo, stats));
@@ -189,22 +195,45 @@
     /** Streams this statement's rows; every statement has already run by the 
time this is reached. */
     private void printRows(PrintWriter pw, ResultReader reader, ResultSetInfo 
resultSetInfo, Stats stats)
             throws HyracksDataException {
+        PrintWriter rows = beginRows(pw);
+        try {
+            ResultUtil.printResults(appCtx, reader, undecorated(rows), stats, 
resultSetInfo.getRecordType());
+        } finally {
+            rows.flush();
+        }
+    }
+
+    /**
+     * Prints the plan an EXPLAIN produced as this statement's rows, the way 
the flat response prints it. The quoting
+     * the plan format calls for is put back afterwards, so it does not follow 
the session into the next statement.
+     */
+    private void printExplain(PrintWriter pw, String explainResult) throws 
HyracksDataException {
+        PrintWriter rows = beginRows(pw);
+        boolean quoteRecord = 
sessionOutput.config().is(SessionConfig.FORMAT_QUOTE_RECORD);
+        try {
+            new ExplainOnlyResultsPrinter(appCtx, explainResult, 
undecorated(rows)).print(rows);
+        } finally {
+            sessionOutput.config().set(SessionConfig.FORMAT_QUOTE_RECORD, 
quoteRecord);
+            rows.flush();
+        }
+    }
+
+    /** Names the rows field and returns the writer they are printed to. */
+    private static PrintWriter beginRows(PrintWriter pw) {
         pw.print(FIELD_INDENT);
         pw.print(quoted(ResultsPrinter.FIELD_NAME));
         pw.print(": ");
-        PrintWriter rows = new PrintWriter(pw) {
+        return new PrintWriter(pw) {
             @Override
             public void println(String x) {
                 print(x);
             }
         };
-        // no result decorators: this printer names the field itself, so the 
decorators that name and number it must not
-        SessionOutput undecorated = new SessionOutput(sessionOutput.config(), 
rows);
-        try {
-            ResultUtil.printResults(appCtx, reader, undecorated, stats, 
resultSetInfo.getRecordType());
-        } finally {
-            rows.flush();
-        }
+    }
+
+    /** No result decorators: this printer names the field itself, so the ones 
that name and number it must not. */
+    private SessionOutput undecorated(PrintWriter rows) {
+        return new SessionOutput(sessionOutput.config(), rows);
     }

     private static void printNested(PrintWriter pw, IResponseFieldPrinter 
printer) throws HyracksDataException {
diff --git 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/result/NcStatementsPrinterTest.java
 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/result/NcStatementsPrinterTest.java
index f3bcec9..21388bd 100644
--- 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/result/NcStatementsPrinterTest.java
+++ 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/result/NcStatementsPrinterTest.java
@@ -24,16 +24,21 @@
 import java.util.List;

 import org.apache.asterix.app.result.fields.NcStatementsPrinter;
+import org.apache.asterix.common.api.IApplicationContext;
+import org.apache.asterix.common.config.CompilerProperties;
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.lang.common.base.Statement;
+import org.apache.asterix.translator.ExecutionPlans;
 import org.apache.asterix.translator.IStatementExecutor.ResultDelivery;
 import org.apache.asterix.translator.IStatementExecutor.StatementInfo;
 import org.apache.asterix.translator.IStatementExecutor.Stats;
 import org.apache.asterix.translator.SessionConfig;
 import org.apache.asterix.translator.SessionOutput;
+import org.apache.hyracks.util.StorageUtil;
 import org.junit.Assert;
 import org.junit.Test;
+import org.mockito.Mockito;

 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -95,6 +100,22 @@
         Assert.assertEquals(statements.toString(), "DESCRIBE LINK", 
statements.get(0).get("kind").asText());
     }

+    /** An EXPLAIN has no result set: the plan it produced is reported as that 
statement's rows. */
+    @Test
+    public void anExplainReportsItsPlanAsItsResult() throws Exception {
+        StatementInfo explain = statement(1, Statement.Kind.QUERY);
+        ExecutionPlans plans = new ExecutionPlans();
+        plans.setExplainResult("{ \"operator\": \"distribute-result\" }");
+        explain.setPlans(plans);
+        JsonNode statements = print(List.of(explain, statement(2, 
Statement.Kind.QUERY)));
+
+        JsonNode results = statements.get(0).get("results");
+        Assert.assertNotNull(statements.toString(), results);
+        Assert.assertEquals(statements.toString(), "distribute-result", 
results.get(0).get("operator").asText());
+        // a statement that produced no plan of its own reports no rows
+        Assert.assertNull(statements.toString(), 
statements.get(1).get("results"));
+    }
+
     private static StatementInfo statement(int position, Statement.Kind kind) {
         StatementInfo statementInfo = new StatementInfo(position, kind, null);
         statementInfo.setStats(new Stats());
@@ -105,13 +126,26 @@
     private static JsonNode print(List<StatementInfo> statements) throws 
Exception {
         StringWriter out = new StringWriter();
         PrintWriter pw = new PrintWriter(out);
-        SessionOutput sessionOutput = new SessionOutput(new 
SessionConfig(SessionConfig.OutputFormat.CLEAN_JSON), pw);
-        new NcStatementsPrinter(null, statements, null, 
ResultDelivery.IMMEDIATE, sessionOutput, StandardCharsets.UTF_8,
-                false, "test-request").print(pw);
+        SessionConfig config = new 
SessionConfig(SessionConfig.OutputFormat.CLEAN_JSON, 
SessionConfig.PlanFormat.JSON);
+        // as the servlet configures it: rows are wrapped in an array
+        config.set(SessionConfig.FORMAT_WRAPPER_ARRAY, true);
+        SessionOutput sessionOutput = new SessionOutput(config, pw);
+        new NcStatementsPrinter(appCtx(), statements, null, 
ResultDelivery.IMMEDIATE, sessionOutput,
+                StandardCharsets.UTF_8, false, "test-request").print(pw);
         pw.flush();
         JsonNode response = OBJECT_MAPPER.readTree("{\n" + out + "\n}");
         JsonNode statementsField = 
response.get(NcStatementsPrinter.FIELD_NAME);
         Assert.assertNotNull(out.toString(), statementsField);
         return statementsField;
     }
+
+    /** Only the frame size is reached, by the printer that writes an explain 
plan. */
+    private static IApplicationContext appCtx() {
+        IApplicationContext appCtx = Mockito.mock(IApplicationContext.class);
+        CompilerProperties compilerProperties = 
Mockito.mock(CompilerProperties.class);
+        
Mockito.when(appCtx.getCompilerProperties()).thenReturn(compilerProperties);
+        Mockito.when(compilerProperties.getFrameSize())
+                .thenReturn(StorageUtil.getIntSizeInBytes(32, 
StorageUtil.StorageUnit.KILOBYTE));
+        return appCtx;
+    }
 }

--
To view, visit https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/21689?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://asterix-gerrit.ics.uci.edu/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: asterixdb
Gerrit-Branch: master
Gerrit-Change-Id: I9673ff75c18d3113b51a9cf752986832a3be9e0b
Gerrit-Change-Number: 21689
Gerrit-PatchSet: 1
Gerrit-Owner: Janhavi Tripurwar <[email protected]>

Reply via email to