Ian Maxon has submitted this change and it was merged. Change subject: [UI] Allow logical plan to be viewed as JSON / formatted JSON ......................................................................
[UI] Allow logical plan to be viewed as JSON / formatted JSON - user model changes: no - storage format changes: no - interface changes: enhancements to the web interface details: Added drop-down menu for printing logical plan and optimized logical plan in string,json, and clean-json. Change-Id: I4dd62e355048a5b8a02e074049fe41e73e74e357 Reviewed-on: https://asterix-gerrit.ics.uci.edu/1885 Integration-Tests: Jenkins <[email protected]> Tested-by: Jenkins <[email protected]> Contrib: Jenkins <[email protected]> Reviewed-by: Till Westmann <[email protected]> Reviewed-by: Ian Maxon <[email protected]> --- M asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionConfig.java M asterixdb/asterix-app/pom.xml M asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java M asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ApiServlet.java M asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java M asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/RestApiServlet.java M asterixdb/asterix-app/src/main/java/org/apache/asterix/api/java/AsterixJavaClient.java M asterixdb/asterix-app/src/main/java/org/apache/asterix/drivers/AsterixClientDriver.java M asterixdb/asterix-app/src/main/resources/webui/querytemplate.html M asterixdb/asterix-app/src/main/resources/webui/static/css/style.css A asterixdb/asterix-app/src/test/java/org/apache/asterix/test/jsonplan/JsonLogicalPlanTest.java A asterixdb/asterix-app/src/test/java/org/apache/asterix/test/jsonplan/JsonOptimizedLogicalPlanTest.java M hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractLogicalOperator.java A hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/AbstractLogicalOperatorPrettyPrintVisitor.java M hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java A hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitorJson.java M hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/PlanPrettyPrinter.java M hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/visitors/ILogicalOperatorVisitor.java 18 files changed, 1,259 insertions(+), 217 deletions(-) Approvals: Anon. E. Moose #1000171: Till Westmann: Looks good to me, approved Jenkins: Verified; ; Verified Ian Maxon: Looks good to me, approved Objections: Jenkins: Violations found diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionConfig.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionConfig.java index cfd4e87..6e67612 100644 --- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionConfig.java +++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/SessionConfig.java @@ -21,6 +21,9 @@ import java.io.Serializable; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + /** * SessionConfig captures several different parameters for controlling @@ -36,7 +39,6 @@ * <li>It allows you to specify output format-specific parameters. */ public class SessionConfig implements Serializable { - private static final long serialVersionUID = 1L; /** @@ -47,6 +49,29 @@ CSV, CLEAN_JSON, LOSSLESS_JSON + }; + + /** + * Used to specify the format for logical plan and optimized logical plan. + */ + + public enum PlanFormat { + JSON, + STRING; + public static PlanFormat get(String fmtString, String label, PlanFormat defaultFmt, Logger logger) { + try { + if (fmtString != null) { + String format = + ("JSON".equalsIgnoreCase(fmtString) || "CLEAN_JSON".equalsIgnoreCase(fmtString)) + ? "JSON" + : fmtString; + return PlanFormat.valueOf(format); + } + } catch (IllegalArgumentException e) { + logger.log(Level.INFO, fmtString + ": unsupported " + label + ", using " + defaultFmt + "instead", e); + } + return defaultFmt; + } }; /** @@ -106,6 +131,7 @@ // Output format. private final OutputFormat fmt; + private final PlanFormat lpfmt; // Standard execution flags. private final boolean executeQuery; @@ -116,13 +142,18 @@ private final Map<String, Boolean> flags; public SessionConfig(OutputFormat fmt) { - this(fmt, true, true, true); + this(fmt, PlanFormat.STRING); + } + + public SessionConfig(OutputFormat fmt, PlanFormat lpfmt) { + this(fmt, true, true, true, lpfmt); } /** * Create a SessionConfig object with all optional values set to defaults: * - All format flags set to "false". * - All out-of-band outputs set to "false". + * * @param fmt * Output format for execution output. * @param optimize @@ -131,13 +162,20 @@ * Whether to execute the query or not. * @param generateJobSpec * Whether to generate the Hyracks job specification (if + * @param lpfmt + * Plan format for logical plan. */ public SessionConfig(OutputFormat fmt, boolean optimize, boolean executeQuery, boolean generateJobSpec) { + this(fmt, optimize, executeQuery, generateJobSpec, PlanFormat.STRING); + } + public SessionConfig(OutputFormat fmt, boolean optimize, boolean executeQuery, boolean generateJobSpec, + PlanFormat lpfmt) { this.fmt = fmt; this.optimize = optimize; this.executeQuery = executeQuery; this.generateJobSpec = generateJobSpec; this.flags = new HashMap<>(); + this.lpfmt = lpfmt; } /** @@ -148,6 +186,13 @@ } /** + * Retrieve the PlanFormat for this execution. + */ + public PlanFormat getLpfmt() { + return this.lpfmt; + } + + /** * Retrieve the value of the "execute query" flag. */ public boolean isExecuteQuery() { diff --git a/asterixdb/asterix-app/pom.xml b/asterixdb/asterix-app/pom.xml index ee733d9..b039071 100644 --- a/asterixdb/asterix-app/pom.xml +++ b/asterixdb/asterix-app/pom.xml @@ -279,6 +279,10 @@ </profiles> <dependencies> <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> <groupId>org.apache.hyracks</groupId> <artifactId>hyracks-control-cc</artifactId> </dependency> 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 567d587..8290446 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 @@ -84,8 +84,10 @@ import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionTypeComputer; import org.apache.hyracks.algebricks.core.algebra.expressions.IMergeAggregationExpressionFactory; import org.apache.hyracks.algebricks.core.algebra.expressions.IMissableTypeComputer; +import org.apache.hyracks.algebricks.core.algebra.prettyprint.AbstractLogicalOperatorPrettyPrintVisitor; import org.apache.hyracks.algebricks.core.algebra.prettyprint.AlgebricksAppendable; import org.apache.hyracks.algebricks.core.algebra.prettyprint.LogicalOperatorPrettyPrintVisitor; +import org.apache.hyracks.algebricks.core.algebra.prettyprint.LogicalOperatorPrettyPrintVisitorJson; import org.apache.hyracks.algebricks.core.algebra.prettyprint.PlanPrettyPrinter; import org.apache.hyracks.algebricks.core.rewriter.base.AlgebricksOptimizationContext; import org.apache.hyracks.algebricks.core.rewriter.base.IOptimizationContextFactory; @@ -111,6 +113,8 @@ private static final int MIN_FRAME_LIMIT_FOR_SORT = 3; private static final int MIN_FRAME_LIMIT_FOR_GROUP_BY = 4; private static final int MIN_FRAME_LIMIT_FOR_JOIN = 5; + private static final String LPLAN = "Logical plan"; + private static final String OPLAN = "Optimized logical plan"; // A white list of supported configurable parameters. private static final Set<String> CONFIGURABLE_PARAMETER_NAMES = @@ -156,7 +160,13 @@ private void printPlanPrefix(SessionOutput output, String planName) { if (output.config().is(SessionConfig.FORMAT_HTML)) { output.out().println("<h4>" + planName + ":</h4>"); - output.out().println("<pre>"); + if (LPLAN.equalsIgnoreCase(planName)) { + output.out().println("<pre class = query-plan>"); + } else if (OPLAN.equalsIgnoreCase(planName)) { + output.out().println("<pre class = query-optimized-plan>"); + } else { + output.out().println("<pre>"); + } } else { output.out().println("----------" + planName + ":"); } @@ -219,7 +229,13 @@ printPlanPrefix(output, "Logical plan"); if (rwQ != null || (statement != null && statement.getKind() == Statement.Kind.LOAD)) { - LogicalOperatorPrettyPrintVisitor pvisitor = new LogicalOperatorPrettyPrintVisitor(output.out()); + AbstractLogicalOperatorPrettyPrintVisitor pvisitor; + if (output.config().getLpfmt().equals(SessionConfig.PlanFormat.JSON)) { + pvisitor = new LogicalOperatorPrettyPrintVisitorJson(output.out()); + } else { + pvisitor = new LogicalOperatorPrettyPrintVisitor(output.out()); + + } PlanPrettyPrinter.printPlan(plan, pvisitor, 0); } printPlanPostfix(output); @@ -273,8 +289,13 @@ } else { printPlanPrefix(output, "Optimized logical plan"); if (rwQ != null || (statement != null && statement.getKind() == Statement.Kind.LOAD)) { - LogicalOperatorPrettyPrintVisitor pvisitor = - new LogicalOperatorPrettyPrintVisitor(output.out()); + AbstractLogicalOperatorPrettyPrintVisitor pvisitor; + if (output.config().getLpfmt().equals(SessionConfig.PlanFormat.JSON)) { + pvisitor = new LogicalOperatorPrettyPrintVisitorJson(output.out()); + + } else { + pvisitor = new LogicalOperatorPrettyPrintVisitor(output.out()); + } PlanPrettyPrinter.printPlan(plan, pvisitor, 0); } printPlanPostfix(output); diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ApiServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ApiServlet.java index fbe5852..a29d869 100644 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ApiServlet.java +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/ApiServlet.java @@ -52,6 +52,7 @@ import org.apache.asterix.translator.IStatementExecutorFactory; import org.apache.asterix.translator.SessionConfig; import org.apache.asterix.translator.SessionConfig.OutputFormat; +import org.apache.asterix.translator.SessionConfig.PlanFormat; import org.apache.asterix.translator.SessionOutput; import org.apache.hyracks.api.client.IHyracksClientConnection; import org.apache.hyracks.api.dataset.IHyracksDataset; @@ -67,7 +68,6 @@ import io.netty.handler.codec.http.HttpResponseStatus; public class ApiServlet extends AbstractServlet { - private static final Logger LOGGER = Logger.getLogger(ApiServlet.class.getName()); public static final String HTML_STATEMENT_SEPARATOR = "<!-- BEGIN -->"; @@ -88,18 +88,20 @@ this.componentProvider = componentProvider; } - @Override - protected void post(IServletRequest request, IServletResponse response) { + @Override protected void post(IServletRequest request, IServletResponse response) { // Query language - ILangCompilationProvider compilationProvider = "AQL".equals(request.getParameter("query-language")) - ? aqlCompilationProvider : sqlppCompilationProvider; + ILangCompilationProvider compilationProvider = "AQL".equals(request.getParameter("query-language")) ? + aqlCompilationProvider : + sqlppCompilationProvider; IParserFactory parserFactory = compilationProvider.getParserFactory(); // Output format. PrintWriter out = response.writer(); OutputFormat format; + boolean csvAndHeader = false; String output = request.getParameter("output-format"); + if ("CSV-Header".equals(output)) { output = "CSV"; csvAndHeader = true; @@ -112,6 +114,8 @@ // Default output format format = OutputFormat.CLEAN_JSON; } + PlanFormat planFormat = + PlanFormat.get(request.getParameter("plan-format"), "plan format", PlanFormat.STRING, LOGGER); String query = request.getParameter("query"); String wrapperArray = request.getParameter("wrapper-array"); @@ -144,12 +148,14 @@ } IParser parser = parserFactory.createParser(query); List<Statement> aqlStatements = parser.parse(); - SessionConfig sessionConfig = new SessionConfig(format, true, isSet(executeQuery), true); + SessionConfig sessionConfig = + new SessionConfig(format, true, isSet(executeQuery), true, planFormat); sessionConfig.set(SessionConfig.FORMAT_HTML, true); sessionConfig.set(SessionConfig.FORMAT_CSV_HEADER, csvAndHeader); sessionConfig.set(SessionConfig.FORMAT_WRAPPER_ARRAY, isSet(wrapperArray)); - sessionConfig.setOOBData(isSet(printExprParam), isSet(printRewrittenExprParam), - isSet(printLogicalPlanParam), isSet(printOptimizedLogicalPlanParam), isSet(printJob)); + sessionConfig + .setOOBData(isSet(printExprParam), isSet(printRewrittenExprParam), isSet(printLogicalPlanParam), + isSet(printOptimizedLogicalPlanParam), isSet(printJob)); SessionOutput sessionOutput = new SessionOutput(sessionConfig, out); MetadataManager.INSTANCE.init(); IStatementExecutor translator = statementExectorFactory.create(appCtx, aqlStatements, sessionOutput, diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java index 6a92a26..f8f5c18 100644 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java @@ -114,7 +114,8 @@ CLIENT_ID("client_context_id"), PRETTY("pretty"), MODE("mode"), - TIMEOUT("timeout"); + TIMEOUT("timeout"), + PLAN_FORMAT("plan-format"); private final String str; @@ -236,6 +237,7 @@ SessionOutput.ResultAppender appendStatus = ResultUtil.createResultStatusAppender(); SessionConfig.OutputFormat format = getFormat(param.format); + //TODO:get the parameters from UI.Currently set to clean_json. SessionConfig sessionConfig = new SessionConfig(format); sessionConfig.set(SessionConfig.FORMAT_WRAPPER_ARRAY, true); sessionConfig.set(SessionConfig.FORMAT_INDENT_JSON, param.pretty); diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/RestApiServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/RestApiServlet.java index 3d00d88..117d7fb 100644 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/RestApiServlet.java +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/RestApiServlet.java @@ -46,6 +46,7 @@ import org.apache.asterix.translator.IStatementExecutorFactory; import org.apache.asterix.translator.SessionConfig; import org.apache.asterix.translator.SessionConfig.OutputFormat; +import org.apache.asterix.translator.SessionConfig.PlanFormat; import org.apache.asterix.translator.SessionOutput; import org.apache.hyracks.api.client.IHyracksClientConnection; import org.apache.hyracks.api.dataset.IHyracksDataset; @@ -107,6 +108,8 @@ format = OutputFormat.CSV; } } + PlanFormat planFormat = + PlanFormat.get(request.getParameter("plan-format"), "plan format", PlanFormat.STRING, LOGGER); // If it's JSON, check for the "lossless" flag @@ -117,7 +120,7 @@ SessionOutput.ResultAppender appendHandle = (app, handle) -> app.append("{ \"").append("handle") .append("\":" + " \"").append(handle).append("\" }"); - SessionConfig sessionConfig = new SessionConfig(format); + SessionConfig sessionConfig = new SessionConfig(format, planFormat); // If it's JSON or ADM, check for the "wrapper-array" flag. Default is // "true" for JSON and "false" for ADM. (Not applicable for CSV.) diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/java/AsterixJavaClient.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/java/AsterixJavaClient.java index 675bb9b..58a7f09 100644 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/java/AsterixJavaClient.java +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/java/AsterixJavaClient.java @@ -37,6 +37,7 @@ import org.apache.asterix.translator.IStatementExecutorFactory; import org.apache.asterix.translator.SessionConfig; import org.apache.asterix.translator.SessionConfig.OutputFormat; +import org.apache.asterix.translator.SessionConfig.PlanFormat; import org.apache.asterix.translator.SessionOutput; import org.apache.hyracks.api.client.IHyracksClientConnection; import org.apache.hyracks.api.job.JobSpecification; @@ -80,11 +81,19 @@ } public void compile() throws Exception { - compile(true, false, false, false, false, false, false); + compile(true, false, true, false, false, false, false); } public void compile(boolean optimize, boolean printRewrittenExpressions, boolean printLogicalPlan, boolean printOptimizedPlan, boolean printPhysicalOpsOnly, boolean generateBinaryRuntime, boolean printJob) + throws Exception { + compile(optimize, printRewrittenExpressions, printLogicalPlan, printOptimizedPlan, printPhysicalOpsOnly, + generateBinaryRuntime, printJob, PlanFormat.STRING); + } + + public void compile(boolean optimize, boolean printRewrittenExpressions, boolean printLogicalPlan, + boolean printOptimizedPlan, boolean printPhysicalOpsOnly, boolean generateBinaryRuntime, boolean printJob, + PlanFormat pformat) throws Exception { queryJobSpec = null; dmlJobs = null; @@ -101,7 +110,7 @@ List<Statement> statements = parser.parse(); MetadataManager.INSTANCE.init(); - SessionConfig conf = new SessionConfig(OutputFormat.ADM, optimize, true, generateBinaryRuntime); + SessionConfig conf = new SessionConfig(OutputFormat.ADM, optimize, true, generateBinaryRuntime, pformat); conf.setOOBData(false, printRewrittenExpressions, printLogicalPlan, printOptimizedPlan, printJob); if (printPhysicalOpsOnly) { conf.set(SessionConfig.FORMAT_ONLY_PHYSICAL_OPS, true); diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/drivers/AsterixClientDriver.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/drivers/AsterixClientDriver.java index ba44833..14a5fe0 100644 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/drivers/AsterixClientDriver.java +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/drivers/AsterixClientDriver.java @@ -53,8 +53,8 @@ } boolean exec = new Boolean(acc.execute); IHyracksClientConnection hcc = exec ? new HyracksConnection("localhost", acc.hyracksPort) : null; - AsterixJavaClient q = compileQuery(hcc, acc.getArguments().get(0), new Boolean(acc.optimize), - new Boolean(acc.onlyPhysical), exec || new Boolean(acc.hyracksJob)); + AsterixJavaClient q = compileQuery(hcc, acc.getArguments().get(0), new Boolean(acc.optimize), false, + exec || new Boolean(acc.hyracksJob)); if (exec) { q.execute(); } @@ -64,8 +64,9 @@ boolean onlyPhysical, boolean createBinaryRuntime) throws Exception { ILangCompilationProvider compilationProvider = new AqlCompilationProvider(); FileReader reader = new FileReader(filename); - AsterixJavaClient q = new AsterixJavaClient(null, hcc, reader, compilationProvider, - new DefaultStatementExecutorFactory(), new StorageComponentProvider()); + AsterixJavaClient q = + new AsterixJavaClient(null, hcc, reader, compilationProvider, new DefaultStatementExecutorFactory(), + new StorageComponentProvider()); q.compile(optimize, true, true, true, onlyPhysical, createBinaryRuntime, createBinaryRuntime); return q; } diff --git a/asterixdb/asterix-app/src/main/resources/webui/querytemplate.html b/asterixdb/asterix-app/src/main/resources/webui/querytemplate.html index 383754e..1157c27 100644 --- a/asterixdb/asterix-app/src/main/resources/webui/querytemplate.html +++ b/asterixdb/asterix-app/src/main/resources/webui/querytemplate.html @@ -19,24 +19,24 @@ <!DOCTYPE html> <html lang="en"> <head> -<meta name="description" content="ASTERIX WEB PAGE" /> -<meta name="viewport" content="width=device-width, initial-scale=1.0"> -<link - href='http://fonts.googleapis.com/css?family=Bitter|PT+Sans+Caption|Open+Sans' - rel='stylesheet' type='text/css'> -<script src="/webui/static/js/jquery.min.js"></script> + <meta name="description" content="ASTERIX WEB PAGE" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link + href='http://fonts.googleapis.com/css?family=Bitter|PT+Sans+Caption|Open+Sans' + rel='stylesheet' type='text/css'> + <script src="/webui/static/js/jquery.min.js"></script> -<link href="/webui/static/css/bootstrap.min.css" rel="stylesheet" - type="text/css" /> -<link href="/webui/static/css/bootstrap-responsive.min.css" - rel="stylesheet" type="text/css" /> + <link href="/webui/static/css/bootstrap.min.css" rel="stylesheet" + type="text/css" /> + <link href="/webui/static/css/bootstrap-responsive.min.css" + rel="stylesheet" type="text/css" /> -<script src="/webui/static/js/bootstrap.min.js"></script> + <script src="/webui/static/js/bootstrap.min.js"></script> -<link href="/webui/static/css/style.css" rel="stylesheet" - type="text/css" /> -<script src="/webui/static/js/jquery.json-viewer.js"></script> -<link href="/webui/static/css/jquery.json-viewer.css" type="text/css" rel="stylesheet" /> + <link href="/webui/static/css/style.css" rel="stylesheet" + type="text/css" /> + <script src="/webui/static/js/jquery.json-viewer.js"></script> + <link href="/webui/static/css/jquery.json-viewer.css" type="text/css" rel="stylesheet" /> <script type="text/javascript"> $(document).ready(function() { @@ -169,13 +169,13 @@ } } - /* Handling Pretty JSON */ + /* Handling Pretty JSON-query result */ var resultFormat = $('#output-format option:checked').text(); if ( resultFormat == 'JSON (formatted)') { $('.result-content').each( function(idx) { var results = $(this).text().split('\n'); - $(this).css('padding-left', '20px'); + $(this).css('padding-left', '20px'); $(this).text(''); for (var iter1 = 0; iter1 < results.length - 1; iter1++) { if (results[iter1].length < 1) { @@ -183,10 +183,36 @@ } var resultJSON = $.parseJSON(results[iter1]); $(this).append($('<div/>').attr("id", "json-record"+idx+"-"+iter1)); - $('#json-record'+idx+"-"+iter1).jsonViewer(resultJSON, {collapsed: true, level: 1}); + $('#json-record'+idx+"-"+iter1).jsonViewer(resultJSON, {collapsed: true, level: 10}); } } ); + } + + + /* Handling Pretty JSON-logical plan */ + var planFormat = $('#plan-format option:checked').text(); + $('.query-plan').addClass("outer"); + $('.query-optimized-plan').addClass("outer"); + if ( planFormat == 'JSON (formatted)') { + $('.query-plan').each( + function() { + var planSt = $(this).text(); + $(this).text(''); + var planJSON = $.parseJSON(planSt); + $(this).append($('<div/>').attr("id", "json-queryPlan")); + $('#json-queryPlan').jsonViewer(planJSON, {collapsed: false, level: 10}); + } + ); + $('.query-optimized-plan').each( + function() { + var opPlanSt = $(this).text(); + $(this).text(''); + var opPlanJSON = $.parseJSON(opPlanSt); + $(this).append($('<div/>').attr("id", "json-queryOptimizedPlan").attr("class","inner")); + $('#json-queryOptimizedPlan').jsonViewer(opPlanJSON, {collapsed: false, level: 10}); + } + ); } @@ -203,106 +229,129 @@ }); </script> -<meta charset=utf-8 /> -<title>AsterixDB Web Interface</title> + <meta charset=utf-8 /> + <title>AsterixDB Web Interface</title> </head> <body> - <div class="navbar navbar-fixed-top"> - <div class="navbar-inner"> - <div class="container"> - <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"> - <span class="icon-bar"></span> - <span class="icon-bar"></span> - <span class="icon-bar"></span> - </a> +<div class="navbar navbar-fixed-top"> + <div class="navbar-inner"> + <div class="container"> + <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </a> - <!-- Temporary logo placeholder --> - <a class="brand" href="#"><img src="/webui/static/img/finalasterixlogo.png"></a> + <!-- Temporary logo placeholder --> + <a class="brand" href="#"><img src="/webui/static/img/finalasterixlogo.png"></a> - <div class="nav-collapse collapse"> - <ul class="nav"> - <li><a href="https://asterixdb.apache.org/" target="_blank"> - Open source<img class="extarget" src="/webui/static/img/targetlink.png"/></a></li> - <li><a href="https://issues.apache.org/jira/browse/ASTERIXDB" target="_blank"> - File issues<img class="extarget" src="/webui/static/img/targetlink.png"/></a></li> - <li><a href="https://ci.apache.org/projects/asterixdb/index.html" target="_blank"> - Documentation<img class="extarget" src="/webui/static/img/targetlink.png"/></a></li> - <li><a href="https://asterixdb.apache.org/community.html" target="_blank"> - Contact<img class="extarget" src="/webui/static/img/targetlink.png"/></a></li> - </ul> - </div><!--/.nav-collapse --> - </div> + <div class="nav-collapse collapse"> + <ul class="nav"> + <li><a href="https://asterixdb.apache.org/" target="_blank"> + Open source<img class="extarget" src="/webui/static/img/targetlink.png"/></a></li> + <li><a href="https://issues.apache.org/jira/browse/ASTERIXDB" target="_blank"> + File issues<img class="extarget" src="/webui/static/img/targetlink.png"/></a></li> + <li><a href="https://ci.apache.org/projects/asterixdb/index.html" target="_blank"> + Documentation<img class="extarget" src="/webui/static/img/targetlink.png"/></a></li> + <li><a href="https://asterixdb.apache.org/community.html" target="_blank"> + Contact<img class="extarget" src="/webui/static/img/targetlink.png"/></a></li> + </ul> + </div><!--/.nav-collapse --> </div> </div> +</div> - <div class="content"> +<div class="content"> <div class="container"> - <div class="row-fluid"> + <div class="row-fluid"> - <div class="span6"> + <div class="span6"> - <form id="queryform" class="form-horizontal" method="post"> - <div style="margin-bottom: 1em;"> - <label class="query">Query</label> - <textarea rows="10" id="qry" name="query" class="query" value="%s" placeholder="Type your query ..."></textarea> + <form id="queryform" class="form-horizontal" method="post"> + <div style="margin-bottom: 1em;"> + <label class="query">Query</label> + <textarea rows="10" id="qry" name="query" class="query" value="%s" + placeholder="Type your query ..."></textarea> + </div> + + <div class="btn-group"> + <button id="checkboxes-on" class="btn"> + <i id="opts" class="icon-ok" style="display:none;"></i>Select Options + </button> + <button id="clear-query-button" class="btn">Clear Query</button> + <!-- <button id="checkboxes-off" class="btn">Clear All Options</button> --> + <button type="submit" id="run-btn" class="btn btn-custom-darken">Run</button> + </div> + + <div> + <label id="query-language" class="optlabel"> Query Language:<br/> + <select name="query-language" class="btn btn-width"> + <option selected value="SQLPP">SQL++</option> + <option value="AQL">AQL</option> + </select> + </label> + <label id="output-format" class="optlabel"> Output Format:<br/> + <select name="output-format" class="btn btn-width"> + <option selected value="CLEAN_JSON">JSON</option> + <option value="CLEAN_JSON">JSON (formatted)</option> + <option value="ADM">ADM</option> + <option value="CSV">CSV (no header)</option> + <option value="CSV-Header">CSV (with header)</option> + <option value="LOSSLESS_JSON">JSON (lossless)</option> + </select> + </label> + <label id="plan-format" class="optlabel"> Plan Format:<br/> + <select name="plan-format" class="btn btn-width"> + <option selected value="JSON">JSON</option> + <option value="CLEAN_JSON">JSON (formatted)</option> + <option value="STRING">String</option> + </select> + </label> + <label class="optlabel"><input type="checkbox" name="wrapper-array" value="true"/> Wrap results + in outer array</label> + <label class="checkbox optlabel"><input type="checkbox" name="print-expr-tree" value="true"/> + Print parsed expressions</label> + <label class="checkbox optlabel"><input type="checkbox" name="print-rewritten-expr-tree" + value="true"/> Print rewritten expressions</label> + <div><label class="checkbox optlabel"><input type="checkbox" + name="print-logical-plan" + value="true"/> Print logical + plan</label></div> + + <div><label class="checkbox optlabel"><input type="checkbox" + name="print-optimized-logical-plan" + value="true"/> Print optimized + logical plan</label></div> + + <label class="checkbox optlabel"><input type="checkbox" name="print-job" value="true"/> Print + Hyracks job</label> + <label class="optlabel"><input type="checkbox" name="execute-query" value="true" checked/> + Execute query</label> + </div> + </form> </div> - <div class="btn-group"> - <button id="checkboxes-on" class="btn"> - <i id="opts" class="icon-ok" style="display:none;"></i>Select Options</button> - <button id="clear-query-button" class="btn">Clear Query</button> - <!-- <button id="checkboxes-off" class="btn">Clear All Options</button> --> - <button type="submit" id="run-btn" class="btn btn-custom-darken">Run</button> + <div class="span6"> + <div class="output"> + <label id="output-heading" class="heading">Output</label> + <div id="output-message" class="message"> + </div> + </div> </div> - - <div> - <label id="query-language" class="optlabel"> Query Language:<br/> - <select name="query-language" class="btn"> - <option selected value="SQLPP">SQL++</option> - <option value="AQL">AQL</option> - </select> - </label> - <label id="output-format" class="optlabel"> Output Format:<br/> - <select name="output-format" class="btn"> - <option selected value="CLEAN_JSON">JSON</option> - <option value="CLEAN_JSON">JSON (formatted)</option> - <option value="ADM">ADM</option> - <option value="CSV">CSV (no header)</option> - <option value="CSV-Header">CSV (with header)</option> - <option value="LOSSLESS_JSON">JSON (lossless)</option> - </select> - </label> - <label class="optlabel"><input type="checkbox" name="wrapper-array" value="true" /> Wrap results in outer array</label> - <label class="checkbox optlabel"><input type="checkbox" name="print-expr-tree" value="true" /> Print parsed expressions</label> - <label class="checkbox optlabel"><input type="checkbox" name="print-rewritten-expr-tree" value="true" /> Print rewritten expressions</label> - <label class="checkbox optlabel"><input type="checkbox" name="print-logical-plan" value="true" /> Print logical plan</label> - <label class="checkbox optlabel"><input type="checkbox" name="print-optimized-logical-plan" value="true" /> Print optimized logical plan</label> - <label class="checkbox optlabel"><input type="checkbox" name="print-job" value="true" /> Print Hyracks job</label> - <label class="optlabel"><input type="checkbox" name="execute-query" value="true" checked/> Execute query</label> - </div> - </form> - </div> - - <div class="span6"> - <div class="output"> - <label id="output-heading" class="heading">Output</label> - <div id="output-message" class="message"> - </div> - </div> - </div> - - </div> + </div> </div> </div> - <div class="footer"> - <section class="line"><hr></section> - <section class="content"> - <section class="left"> - </section> - <section class="right"> - </section> +<div class="footer"> + <section class="line"> + <hr> </section> - </div> + <section class="content"> + <section class="left"> + </section> + <section class="right"> + </section> + </section> +</div> </body> </html> diff --git a/asterixdb/asterix-app/src/main/resources/webui/static/css/style.css b/asterixdb/asterix-app/src/main/resources/webui/static/css/style.css index b9b733c..1982e3a 100644 --- a/asterixdb/asterix-app/src/main/resources/webui/static/css/style.css +++ b/asterixdb/asterix-app/src/main/resources/webui/static/css/style.css @@ -230,3 +230,33 @@ .span6 { padding: 24px; } + +.inline-half { + display: inline-block; + width: 50%; + float:left; +} +.inline-btn-width{ + display: inline-block; + width: 58%; +} + +.btn-width{ + width: 30%; +} +.inner{ + float : none; +} + +pre.outer { + padding-left :20px; + overflow-x : scroll !important; + overflow-y : scroll !important; + word-wrap : normal !important; + word-break : normal !important; +} + +.wrapper { + overflow: hidden; +} + diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/jsonplan/JsonLogicalPlanTest.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/jsonplan/JsonLogicalPlanTest.java new file mode 100644 index 0000000..77d0130 --- /dev/null +++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/jsonplan/JsonLogicalPlanTest.java @@ -0,0 +1 @@ +/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */package org.apache.asterix.test.jsonplan;import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.InputStreamReader;import java.io.PrintWriter; import java.io.Reader;import java.util.ArrayList;import java.util.Collection;import java.util.logging.Logger;import org.apache.asterix.api.common.AsterixHyracksIntegrationUtil;import org.apache.asterix.api.java.AsterixJavaClient;import org.apache.asterix.app.translator.DefaultStatementExecutorFactory;import org.apache.asterix.common.config.GlobalConfig;import org.apache.asterix.common.context.IStorageComponentProvider;import org.apache.asterix.common.dataflow.ICcApplicationContext;import org.apache.asterix.common.exceptions.AsterixException;import org.apache.asterix.compiler.provider.AqlCompilationProvider;import org.apache.asterix.compiler.provider.ILangCompilationProvider;import org.apache.asterix.compiler.provider.SqlppCompilationProvider;import org.apache.asterix.external.util.ExternalDataConstants;import org.apache.asterix.external.util.IdentitiyResolverFactory;import org.apache.asterix.file.StorageComponentProvider;import org.apache.asterix.test.base.Asterix TestHelper;import org.apache.asterix.test.common.TestHelper;import org.apache.asterix.test.runtime.HDFSCluster;import org.apache.asterix.translator.IStatementExecutorFactory;import org.apache.asterix.translator.SessionConfig.PlanFormat;import org.apache.hyracks.api.client.IHyracksClientConnection;import org.junit.AfterClass;import org.junit.Assume;import org.junit.BeforeClass;import org.junit.Test;import org.junit.internal.AssumptionViolatedException;import org.junit.runner.RunWith;import org.junit.runners.Parameterized;import org.junit.runners.Parameterized.Parameters;import com.fasterxml.jackson.core.JsonParser;import com.fasterxml.jackson.databind.ObjectMapper;@RunWith(Parameterized.class)public class JsonLogicalPlanTest { private static final Logger LOGGER = Logger.getLogger(org.apache.asterix.test.jsonplan.JsonLogicalPlanTest.class.getName()); protected static final String SEPARATOR = File.separator; private static final String EXTEN SION_AQL = "aql"; private static final String EXTENSION_SQLPP = "sqlpp"; private static final String EXTENSION_RESULT = "plan"; private static final String FILENAME_IGNORE = "ignore.txt"; private static final String FILENAME_ONLY = "only.txt"; private static final String PATH_BASE = "src" + SEPARATOR + "test" + SEPARATOR + "resources" + SEPARATOR + "optimizerts" + SEPARATOR; private static final String PATH_QUERIES = PATH_BASE + "queries" + SEPARATOR; protected static String PATH_ACTUAL = "target" + File.separator + "jplantest" + SEPARATOR; protected static boolean optimized = false; private static final ArrayList<String> ignore = AsterixTestHelper.readTestListFile(FILENAME_IGNORE, PATH_BASE); private static final ArrayList<String> only = AsterixTestHelper.readTestListFile(FILENAME_ONLY, PATH_BASE); protected static String TEST_CONFIG_FILE_NAME = "asterix-build-configuration.xml"; private static final ILangCompilationProvi der aqlCompilationProvider = new AqlCompilationProvider(); private static final ILangCompilationProvider sqlppCompilationProvider = new SqlppCompilationProvider(); protected static ILangCompilationProvider extensionLangCompilationProvider = null; protected static IStatementExecutorFactory statementExecutorFactory = new DefaultStatementExecutorFactory(); protected static IStorageComponentProvider storageComponentProvider = new StorageComponentProvider(); protected static AsterixHyracksIntegrationUtil integrationUtil = new AsterixHyracksIntegrationUtil(); @BeforeClass public static void setUp() throws Exception { System.setProperty(GlobalConfig.CONFIG_FILE_PROPERTY, TEST_CONFIG_FILE_NAME); final File outdir = new File(PATH_ACTUAL); outdir.mkdirs(); HDFSCluster.getInstance().setup(); integrationUtil.init(true); // Set the node resolver to be the identity resolver that expects node names // to be nod e controller ids; a valid assumption in test environment. System.setProperty(ExternalDataConstants.NODE_RESOLVER_FACTORY_PROPERTY, IdentitiyResolverFactory.class.getName()); } @AfterClass public static void tearDown() throws Exception { File outdir = new File(PATH_ACTUAL); File[] files = outdir.listFiles(); if (files == null || files.length == 0) { outdir.delete(); } HDFSCluster.getInstance().cleanup(); integrationUtil.deinit(true); } private static void suiteBuildPerFile(File file, Collection<Object[]> testArgs, String path) { if (file.isDirectory() && !file.getName().startsWith(".")) { for (File innerfile : file.listFiles()) { String subdir = innerfile.isDirectory() ? path + innerfile.getName() + SEPARATOR : path; suiteBuildPerFile(innerfile, testArgs, subdir); } } if (file.isFile() && (file.getN ame().endsWith(EXTENSION_AQL) || file.getName().endsWith(EXTENSION_SQLPP))) { String resultFileName = AsterixTestHelper.extToResExt(file.getName(), EXTENSION_RESULT); File actualFile = new File(PATH_ACTUAL + SEPARATOR + path + resultFileName); testArgs.add(new Object[] { file, actualFile }); } } @Parameters(name = "JsonLogicalPlanTest {index}: {0}") public static Collection<Object[]> tests() { Collection<Object[]> testArgs = new ArrayList<>(); if (only.isEmpty()) { suiteBuildPerFile(new File(PATH_QUERIES), testArgs, ""); } else { for (String path : only) { suiteBuildPerFile(new File(PATH_QUERIES + path), testArgs, path.lastIndexOf(SEPARATOR) < 0 ? "" : path.substring(0, path.lastIndexOf(SEPARATOR) + 1)); } } return testArgs; } private final File actualFile; private final File queryFile; public J sonLogicalPlanTest(final File queryFile, final File actualFile) { this.queryFile = queryFile; this.actualFile = actualFile; } @Test public void test() throws Exception { try { String queryFileShort = queryFile.getPath().substring(PATH_QUERIES.length()).replace(SEPARATOR.charAt(0), '/'); if (!only.isEmpty()) { boolean toRun = TestHelper.isInPrefixList(only, queryFileShort); if (!toRun) { LOGGER.info("SKIP TEST: \"" + queryFile.getPath() + "\" \"only.txt\" not empty and not in \"only.txt\"."); } Assume.assumeTrue(toRun); } boolean skipped = TestHelper.isInPrefixList(ignore, queryFileShort); if (skipped) { LOGGER.info("SKIP TEST: \"" + queryFile.getPath() + "\" in \"ignore.txt\"."); } Assume.assumeTrue(!skipped); LOGGER.info("RUN TEST: \"" + queryFile.getPath() + "\""); Reader query = new BufferedReader(new InputStreamReader(new FileInputStream(queryFile), "UTF-8")); // Forces the creation of actualFile. actualFile.getParentFile().mkdirs(); PrintWriter plan = new PrintWriter(actualFile); ILangCompilationProvider provider = queryFile.getName().endsWith("aql") ? aqlCompilationProvider : sqlppCompilationProvider; if (extensionLangCompilationProvider != null) { provider = extensionLangCompilationProvider; } IHyracksClientConnection hcc = integrationUtil.getHyracksClientConnection(); AsterixJavaClient asterix = new AsterixJavaClient((ICcApplicationContext) integrationUtil.cc.getApplicationContext(), hcc, query, plan, provider, statementExecutorFactory, storageComponentProvider); try { asterix.compile(true, false, !optimized, optimized, false, false, false, PlanFormat.JSON); } catch (AsterixException e) { plan.close(); query.close(); throw new Exception("Compile ERROR for " + queryFile + ": " + e.getMessage(), e); } plan.close(); query.close(); BufferedReader readerActual = new BufferedReader(new InputStreamReader(new FileInputStream(actualFile), "UTF-8")); String lineActual, objectActual = ""; boolean firstPlan = false; while ((lineActual = readerActual.readLine()) != null) { if (lineActual.contains("--")) { if (firstPlan) { break; } firstPlan = true; } else { objectActual = objectActual + lineActual; } } try { final JsonParser parser = new ObjectMapper().getJsonFactory().createJsonParser(objectActual); while (parser.nextToken() != null) { } } finally { readerActual.close(); } } catch (Exception e) { if (!(e instanceof AssumptionViolatedException)) { LOGGER.severe("Test \"" + queryFile.getPath() + "\" FAILED!"); throw new Exception("Test \"" + queryFile.getPath() + "\" FAILED!", e); } else { throw e; } } }} \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/jsonplan/JsonOptimizedLogicalPlanTest.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/jsonplan/JsonOptimizedLogicalPlanTest.java new file mode 100644 index 0000000..b8e4595 --- /dev/null +++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/jsonplan/JsonOptimizedLogicalPlanTest.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.asterix.test.jsonplan; + +import java.io.File; +import java.util.logging.Logger; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class JsonOptimizedLogicalPlanTest extends JsonLogicalPlanTest { + + private static final Logger LOGGER = + Logger.getLogger(org.apache.asterix.test.jsonplan.JsonOptimizedLogicalPlanTest.class.getName()); + + public JsonOptimizedLogicalPlanTest(File queryFile, File actualFile) { + super(queryFile, actualFile); + optimized = true; + PATH_ACTUAL = "target" + File.separator + "joptplantest" + SEPARATOR; + } +} \ No newline at end of file diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractLogicalOperator.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractLogicalOperator.java index 4686f32..8b38a2b 100644 --- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractLogicalOperator.java +++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/AbstractLogicalOperator.java @@ -19,7 +19,7 @@ package org.apache.hyracks.algebricks.core.algebra.operators.logical; import java.util.ArrayList; -import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; @@ -63,7 +63,7 @@ private AbstractLogicalOperator.ExecutionMode mode = AbstractLogicalOperator.ExecutionMode.UNPARTITIONED; protected IPhysicalOperator physicalOperator; - private final Map<String, Object> annotations = new HashMap<>(); + private final Map<String, Object> annotations = new IdentityHashMap<String, Object>(); private boolean bJobGenEnabled = true; protected final List<Mutable<ILogicalOperator>> inputs; diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/AbstractLogicalOperatorPrettyPrintVisitor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/AbstractLogicalOperatorPrettyPrintVisitor.java new file mode 100644 index 0000000..140ba80 --- /dev/null +++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/AbstractLogicalOperatorPrettyPrintVisitor.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.hyracks.algebricks.core.algebra.prettyprint; + +import org.apache.commons.lang3.mutable.Mutable; +import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; +import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator; +import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan; +import org.apache.hyracks.algebricks.core.algebra.base.IPhysicalOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractOperatorWithNestedPlans; +import org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalExpressionVisitor; +import org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalOperatorVisitor; + +public abstract class AbstractLogicalOperatorPrettyPrintVisitor implements ILogicalOperatorVisitor<Void, Integer> { + ILogicalExpressionVisitor<String, Integer> exprVisitor; + AlgebricksAppendable buffer; + + public AbstractLogicalOperatorPrettyPrintVisitor() { + this(new AlgebricksAppendable()); + } + + public AbstractLogicalOperatorPrettyPrintVisitor(Appendable app) { + this(new AlgebricksAppendable(app), new LogicalExpressionPrettyPrintVisitor()); + } + + public AbstractLogicalOperatorPrettyPrintVisitor(AlgebricksAppendable buffer) { + this(buffer, new LogicalExpressionPrettyPrintVisitor()); + } + + public AbstractLogicalOperatorPrettyPrintVisitor(AlgebricksAppendable buffer, + ILogicalExpressionVisitor<String, Integer> exprVisitor) { + reset(buffer); + this.exprVisitor = exprVisitor; + } + + public AlgebricksAppendable reset(AlgebricksAppendable buffer) { + AlgebricksAppendable old = this.buffer; + this.buffer = buffer; + return old; + } + + public AlgebricksAppendable get() { + return buffer; + } + + @Override + public String toString() { + return buffer.toString(); + } + + CharSequence str(Object o) { + return String.valueOf(o); + } + + protected static void appendln(AlgebricksAppendable buf, String s) throws AlgebricksException { + buf.append(s); + buf.append("\n"); + } + + protected static void append(AlgebricksAppendable buf, String s) throws AlgebricksException { + buf.append(s); + } + + protected static void pad(AlgebricksAppendable buf, int indent) throws AlgebricksException { + for (int i = 0; i < indent; ++i) { + buf.append(' '); + } + } + + public static void printPhysicalOperator(AbstractLogicalOperator op, int indent, AlgebricksAppendable out) + throws AlgebricksException { + IPhysicalOperator pOp = op.getPhysicalOperator(); + pad(out, indent); + appendln(out, "-- " + pOp.toString() + " |" + op.getExecutionMode() + "|"); + if (op.hasNestedPlans()) { + AbstractOperatorWithNestedPlans opNest = (AbstractOperatorWithNestedPlans) op; + for (ILogicalPlan p : opNest.getNestedPlans()) { + pad(out, indent + 8); + appendln(out, "{"); + printPhysicalOps(p, out, indent + 10); + pad(out, indent + 8); + appendln(out, "}"); + } + } + for (Mutable<ILogicalOperator> i : op.getInputs()) { + printPhysicalOperator((AbstractLogicalOperator) i.getValue(), indent + 2, out); + } + } + + public static void printPhysicalOps(ILogicalPlan plan, AlgebricksAppendable out, int indent) + throws AlgebricksException { + for (Mutable<ILogicalOperator> root : plan.getRoots()) { + printPhysicalOperator((AbstractLogicalOperator) root.getValue(), indent, out); + } + } +} diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java index 2139627..fe63b89 100644 --- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java +++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java @@ -25,18 +25,21 @@ import org.apache.hyracks.algebricks.common.utils.Pair; import org.apache.hyracks.algebricks.common.utils.Triple; import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression; +import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator; import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan; +import org.apache.hyracks.algebricks.core.algebra.base.IPhysicalOperator; import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractOperatorWithNestedPlans; import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractUnnestMapOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.AggregateOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.AssignOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.DataSourceScanOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.DelegateOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.DistinctOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.DistributeResultOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.EmptyTupleSourceOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.ExchangeOperator; -import org.apache.hyracks.algebricks.core.algebra.operators.logical.DelegateOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.GroupByOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.IndexInsertDeleteUpsertOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator; @@ -65,48 +68,50 @@ import org.apache.hyracks.algebricks.core.algebra.operators.logical.WriteOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.WriteResultOperator; import org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalExpressionVisitor; -import org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalOperatorVisitor; -public class LogicalOperatorPrettyPrintVisitor implements ILogicalOperatorVisitor<Void, Integer> { - - ILogicalExpressionVisitor<String, Integer> exprVisitor; - AlgebricksAppendable buffer; +public class LogicalOperatorPrettyPrintVisitor extends AbstractLogicalOperatorPrettyPrintVisitor { public LogicalOperatorPrettyPrintVisitor() { - this(new AlgebricksAppendable()); - } - - public LogicalOperatorPrettyPrintVisitor(Appendable app) { - this(new AlgebricksAppendable(app), new LogicalExpressionPrettyPrintVisitor()); - } - - public LogicalOperatorPrettyPrintVisitor(AlgebricksAppendable buffer) { - this(buffer, new LogicalExpressionPrettyPrintVisitor()); + super(); } public LogicalOperatorPrettyPrintVisitor(AlgebricksAppendable buffer, ILogicalExpressionVisitor<String, Integer> exprVisitor) { - reset(buffer); - this.exprVisitor = exprVisitor; + super(buffer, exprVisitor); } - public AlgebricksAppendable reset(AlgebricksAppendable buffer) { - AlgebricksAppendable old = this.buffer; - this.buffer = buffer; - return old; + public LogicalOperatorPrettyPrintVisitor(AlgebricksAppendable buffer) { + super(buffer); } - public AlgebricksAppendable get() { - return buffer; + public LogicalOperatorPrettyPrintVisitor(Appendable app) { + super(app); } - @Override - public String toString() { - return buffer.toString(); + public static void printPlan(ILogicalPlan plan, LogicalOperatorPrettyPrintVisitor pvisitor, int indent) + throws AlgebricksException { + for (Mutable<ILogicalOperator> root : plan.getRoots()) { + printOperator((AbstractLogicalOperator) root.getValue(), pvisitor, indent); + } } - CharSequence str(Object o) { - return String.valueOf(o); + public static void printOperator(AbstractLogicalOperator op, LogicalOperatorPrettyPrintVisitor pvisitor, int indent) + throws AlgebricksException { + final AlgebricksAppendable out = pvisitor.get(); + op.accept(pvisitor, indent); + IPhysicalOperator pOp = op.getPhysicalOperator(); + + if (pOp != null) { + out.append("\n"); + pad(out, indent); + appendln(out, "-- " + pOp.toString() + " |" + op.getExecutionMode() + "|"); + } else { + appendln(out, " -- |" + op.getExecutionMode() + "|"); + } + + for (Mutable<ILogicalOperator> i : op.getInputs()) { + printOperator((AbstractLogicalOperator) i.getValue(), pvisitor, indent + 2); + } } @Override @@ -480,7 +485,7 @@ } else { addIndent(indent).append(" {\n"); } - PlanPrettyPrinter.printPlan(p, this, indent + 10); + printPlan(p, this, indent + 10); addIndent(indent).append(" }"); } } diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitorJson.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitorJson.java new file mode 100644 index 0000000..fefb1e9 --- /dev/null +++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitorJson.java @@ -0,0 +1,748 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.hyracks.algebricks.core.algebra.prettyprint; + +import java.util.Deque; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.mutable.Mutable; +import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; +import org.apache.hyracks.algebricks.common.utils.Pair; +import org.apache.hyracks.algebricks.common.utils.Triple; +import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression; +import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator; +import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan; +import org.apache.hyracks.algebricks.core.algebra.base.IPhysicalOperator; +import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractOperatorWithNestedPlans; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractUnnestMapOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.AggregateOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.AssignOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.DataSourceScanOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.DelegateOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.DistinctOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.DistributeResultOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.EmptyTupleSourceOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.ExchangeOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.GroupByOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.IndexInsertDeleteUpsertOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.InsertDeleteUpsertOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.InsertDeleteUpsertOperator.Kind; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.IntersectOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterJoinOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterUnnestMapOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterUnnestOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.LimitOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.MaterializeOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.NestedTupleSourceOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.ProjectOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.ReplicateOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.RunningAggregateOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.ScriptOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.SelectOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.SinkOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.SplitOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.SubplanOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.TokenizeOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnionAllOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestMapOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.WriteOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.WriteResultOperator; +import org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalExpressionVisitor; + +public class LogicalOperatorPrettyPrintVisitorJson extends AbstractLogicalOperatorPrettyPrintVisitor { + Map<AbstractLogicalOperator, String> operatorIdentity = new HashMap<>(); + + public LogicalOperatorPrettyPrintVisitorJson(Appendable app) { + super(app); + } + + IdCounter idCounter = new IdCounter(); + + public class IdCounter { + private int id; + private Deque<Integer> prefix; + + public IdCounter() { + prefix = new LinkedList<Integer>(); + prefix.add(1); + this.id = 0; + } + + public void previousPrefix() { + this.id = prefix.removeLast(); + } + + public void nextPrefix() { + prefix.add(this.id); + this.id = 0; + } + + public String printOperatorId(AbstractLogicalOperator op) { + String stringPrefix = ""; + Object[] values = this.prefix.toArray(); + for (Object val : values) { + stringPrefix = stringPrefix.isEmpty() ? val.toString() : stringPrefix + "." + val.toString(); + } + if (!operatorIdentity.containsKey(op)) { + String opId = stringPrefix.isEmpty() ? "" + Integer.toString(++id) + : stringPrefix + "." + Integer.toString(++id); + operatorIdentity.put(op, opId); + } + return operatorIdentity.get(op); + } + } + + public static void printPlanJson(ILogicalPlan plan, LogicalOperatorPrettyPrintVisitorJson pvisitor, int indent) + throws AlgebricksException { + for (Mutable<ILogicalOperator> root : plan.getRoots()) { + printOperatorJson((AbstractLogicalOperator) root.getValue(), pvisitor, indent); + } + } + + public static void printOperatorJson(AbstractLogicalOperator op, LogicalOperatorPrettyPrintVisitorJson pvisitor, + int indent) throws AlgebricksException { + int currentIndent = indent; + final AlgebricksAppendable out = pvisitor.get(); + pad(out, currentIndent); + appendln(out, "{"); + currentIndent++; + op.accept(pvisitor, currentIndent); + appendln(out, ","); + pad(out, currentIndent); + append(out, "\"operatorId\" : \"" + pvisitor.idCounter.printOperatorId(op) + "\""); + IPhysicalOperator pOp = op.getPhysicalOperator(); + if (pOp != null) { + appendln(out, ","); + pad(out, currentIndent); + String pOperator = "\"physical-operator\":\"" + pOp.toString() + "\""; + append(out, pOperator); + } + appendln(out, ","); + pad(out, currentIndent); + append(out, "\"execution-mode\":\"" + op.getExecutionMode() + '"'); + if (!op.getInputs().isEmpty()) { + appendln(out, ","); + pad(out, currentIndent); + appendln(out, "\"inputs\":["); + boolean moreInputes = false; + for (Mutable<ILogicalOperator> k : op.getInputs()) { + if (moreInputes) { + append(out, ","); + } + printOperatorJson((AbstractLogicalOperator) k.getValue(), pvisitor, currentIndent + 4); + moreInputes = true; + } + pad(out, currentIndent + 2); + appendln(out, "]"); + } + out.append("\n"); + pad(out, currentIndent - 1); + appendln(out, "}"); + } + + public void variablePrintHelper(List<LogicalVariable> variables, Integer indent) throws AlgebricksException { + if (!variables.isEmpty()) { + addIndent(0).append(",\n"); + addIndent(indent).append("\"variables\" :["); + boolean first = true; + for (LogicalVariable v : variables) { + if (!first) { + buffer.append(","); + } + buffer.append("\"" + str(v) + "\""); + first = false; + } + buffer.append("]"); + } + } + + @Override + public Void visitAggregateOperator(AggregateOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"aggregate\""); + variablePrintHelper(op.getVariables(), indent); + + return null; + } + + @Override + public Void visitRunningAggregateOperator(RunningAggregateOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"running-aggregate\""); + variablePrintHelper(op.getVariables(), indent); + if (!op.getExpressions().isEmpty()) { + addIndent(0).append(",\n"); + pprintExprList(op.getExpressions(), indent); + } + return null; + } + + @Override + public Void visitEmptyTupleSourceOperator(EmptyTupleSourceOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"empty-tuple-source\""); + return null; + } + + @Override + public Void visitGroupByOperator(GroupByOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"group-by\""); + + if (op.isGroupAll()) { + buffer.append(",\n"); + addIndent(indent).append("\"option\":\"all\""); + } + if (!op.getGroupByList().isEmpty()) { + buffer.append(",\n"); + addIndent(indent).append("\"group-by-list\":"); + pprintVeList(op.getGroupByList(), indent); + } + if (!op.getDecorList().isEmpty()) { + buffer.append(",\n"); + addIndent(indent).append("\"decor-list\":"); + pprintVeList(op.getDecorList(), indent); + } + if (!op.getNestedPlans().isEmpty()) { + buffer.append(",\n"); + addIndent(indent).append("\"subplan\":"); + printNestedPlans(op, indent); + } + return null; + } + + @Override + public Void visitDistinctOperator(DistinctOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"distinct\""); + if (!op.getExpressions().isEmpty()) { + addIndent(0).append(",\n"); + pprintExprList(op.getExpressions(), indent); + } + return null; + } + + @Override + public Void visitInnerJoinOperator(InnerJoinOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"join\",\n"); + addIndent(indent) + .append("\"condition\":" + "\"" + op.getCondition().getValue().accept(exprVisitor, indent) + "\""); + return null; + } + + @Override + public Void visitLeftOuterJoinOperator(LeftOuterJoinOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"left-outer-join\",\n"); + addIndent(indent) + .append("\"condition\":" + "\"" + op.getCondition().getValue().accept(exprVisitor, indent) + "\""); + return null; + } + + @Override + public Void visitNestedTupleSourceOperator(NestedTupleSourceOperator op, Integer indent) + throws AlgebricksException { + addIndent(indent).append("\"operator\":\"nested-tuple-source\""); + return null; + } + + @Override + public Void visitOrderOperator(OrderOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"order\""); + for (Pair<OrderOperator.IOrder, Mutable<ILogicalExpression>> p : op.getOrderExpressions()) { + buffer.append(",\n"); + if (op.getTopK() != -1) { + addIndent(indent).append("\"topK\":\"" + op.getTopK() + "\",\n"); + } + String fst = getOrderString(p.first); + addIndent(indent).append("\"first\":" + fst + ",\n"); + addIndent(indent) + .append("\"second\":\"" + p.second.getValue().accept(exprVisitor, indent).replace('"', ' ') + "\""); + } + return null; + } + + private String getOrderString(OrderOperator.IOrder first) { + switch (first.getKind()) { + case ASC: + return "\"ASC\""; + case DESC: + return "\"DESC\""; + default: + return first.getExpressionRef().toString(); + } + } + + @Override + public Void visitAssignOperator(AssignOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"assign\""); + variablePrintHelper(op.getVariables(), indent); + if (!op.getExpressions().isEmpty()) { + addIndent(0).append(",\n"); + pprintExprList(op.getExpressions(), indent); + } + return null; + } + + @Override + public Void visitWriteOperator(WriteOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"write\""); + if (!op.getExpressions().isEmpty()) { + addIndent(0).append(",\n"); + pprintExprList(op.getExpressions(), indent); + } + return null; + } + + @Override + public Void visitDistributeResultOperator(DistributeResultOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"distribute-result\""); + if (!op.getExpressions().isEmpty()) { + addIndent(0).append(",\n"); + pprintExprList(op.getExpressions(), indent); + } + return null; + } + + @Override + public Void visitWriteResultOperator(WriteResultOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"load\",\n"); + addIndent(indent).append(str(op.getDataSource())).append("\"from\":") + .append(op.getPayloadExpression().getValue().accept(exprVisitor, indent) + ",\n"); + addIndent(indent).append("\"partitioned-by\":{"); + pprintExprList(op.getKeyExpressions(), indent); + addIndent(indent).append("}"); + return null; + } + + @Override + public Void visitSelectOperator(SelectOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"select\",\n"); + addIndent(indent).append("\"expressions\":\"" + + op.getCondition().getValue().accept(exprVisitor, indent).replace('"', ' ') + "\""); + return null; + } + + @Override + public Void visitProjectOperator(ProjectOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"project\""); + variablePrintHelper(op.getVariables(), indent); + return null; + } + + @Override + public Void visitSubplanOperator(SubplanOperator op, Integer indent) throws AlgebricksException { + if (!op.getNestedPlans().isEmpty()) { + addIndent(indent).append("\"subplan\":"); + printNestedPlans(op, indent); + } + return null; + } + + @Override + public Void visitUnionOperator(UnionAllOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"union\""); + for (Triple<LogicalVariable, LogicalVariable, LogicalVariable> v : op.getVariableMappings()) { + buffer.append(",\n"); + addIndent(indent) + .append("\"values\":[" + "\"" + v.first + "\"," + "\"" + v.second + "\"," + "\"" + v.third + "\"]"); + } + return null; + } + + @Override + public Void visitIntersectOperator(IntersectOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"intersect\",\n"); + + addIndent(indent).append("\"output-variables\":["); + for (int i = 0; i < op.getOutputVars().size(); i++) { + if (i > 0) { + buffer.append(", "); + } + buffer.append("\"" + str(op.getOutputVars().get(i)) + "\""); + } + buffer.append("],"); + addIndent(indent).append("\"input_variables\":["); + for (int i = 0; i < op.getNumInput(); i++) { + if (i > 0) { + buffer.append(",\n"); + } + buffer.append("["); + for (int j = 0; j < op.getInputVariables(i).size(); j++) { + if (j > 0) { + buffer.append(", "); + } + buffer.append("\"" + str(op.getInputVariables(i).get(j)) + "\""); + } + buffer.append(']'); + } + buffer.append("]"); + return null; + } + + @Override + public Void visitUnnestOperator(UnnestOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"unnest\""); + variablePrintHelper(op.getVariables(), indent); + if (op.getPositionalVariable() != null) { + buffer.append(",\n"); + addIndent(indent).append("\"position\":\"" + op.getPositionalVariable() + "\""); + } + buffer.append(",\n"); + addIndent(indent).append("\"expressions\":\"" + + op.getExpressionRef().getValue().accept(exprVisitor, indent).replace('"', ' ') + "\""); + return null; + } + + @Override + public Void visitLeftOuterUnnestOperator(LeftOuterUnnestOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"outer-unnest\",\n"); + addIndent(indent).append("\"variables\":[\"" + op.getVariable() + "\"]"); + if (op.getPositionalVariable() != null) { + buffer.append(",\n"); + addIndent(indent).append("\"position\":" + op.getPositionalVariable()); + } + buffer.append(",\n"); + addIndent(indent).append("\"expressions\":\"" + + op.getExpressionRef().getValue().accept(exprVisitor, indent).replace('"', ' ') + "\""); + return null; + } + + @Override + public Void visitUnnestMapOperator(UnnestMapOperator op, Integer indent) throws AlgebricksException { + return printAbstractUnnestMapOperator(op, indent, "unnest-map"); + } + + @Override + public Void visitLeftOuterUnnestMapOperator(LeftOuterUnnestMapOperator op, Integer indent) + throws AlgebricksException { + return printAbstractUnnestMapOperator(op, indent, "left-outer-unnest-map"); + } + + private Void printAbstractUnnestMapOperator(AbstractUnnestMapOperator op, Integer indent, String opSignature) + throws AlgebricksException { + AlgebricksAppendable plan = addIndent(indent).append("\"operator\":\"" + opSignature + "\""); + variablePrintHelper(op.getVariables(), indent); + buffer.append(",\n"); + addIndent(indent).append("\"expressions\":\"" + + op.getExpressionRef().getValue().accept(exprVisitor, indent).replace('"', ' ') + "\""); + appendFilterInformation(plan, op.getMinFilterVars(), op.getMaxFilterVars(), indent); + return null; + } + + @Override + public Void visitDataScanOperator(DataSourceScanOperator op, Integer indent) throws AlgebricksException { + AlgebricksAppendable plan = addIndent(indent).append("\"operator\":\"data-scan\""); + if (!op.getProjectVariables().isEmpty()) { + addIndent(0).append(",\n"); + addIndent(indent).append("\"project-variables\":["); + boolean first = true; + for (LogicalVariable v : op.getProjectVariables()) { + if (!first) { + buffer.append(","); + } + buffer.append("\"" + str(v) + "\""); + first = false; + } + buffer.append("]"); + } + variablePrintHelper(op.getVariables(), indent); + if (op.getDataSource() != null) { + addIndent(0).append(",\n"); + addIndent(indent).append("\"data-source\":\"" + op.getDataSource() + "\""); + } + appendFilterInformation(plan, op.getMinFilterVars(), op.getMaxFilterVars(), indent); + return null; + } + + private Void appendFilterInformation(AlgebricksAppendable plan, List<LogicalVariable> minFilterVars, + List<LogicalVariable> maxFilterVars, Integer indent) throws AlgebricksException { + if (minFilterVars != null || maxFilterVars != null) { + plan.append(",\n"); + addIndent(indent); + plan.append("\"with-filter-on\":{"); + } + if (minFilterVars != null) { + buffer.append("\n"); + addIndent(indent).append("\"min\":["); + boolean first = true; + for (LogicalVariable v : minFilterVars) { + if (!first) { + buffer.append(","); + } + buffer.append("\"" + str(v) + "\""); + first = false; + } + buffer.append("]"); + } + if (minFilterVars != null && maxFilterVars != null) { + buffer.append(","); + } + if (maxFilterVars != null) { + buffer.append("\n"); + addIndent(indent).append("\"max\":["); + boolean first = true; + for (LogicalVariable v : maxFilterVars) { + if (!first) { + buffer.append(","); + } + buffer.append("\"" + str(v) + "\""); + first = false; + } + buffer.append("]"); + } + if (minFilterVars != null || maxFilterVars != null) { + plan.append("\n"); + addIndent(indent).append("}"); + } + return null; + } + + @Override + public Void visitLimitOperator(LimitOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"limit\",\n"); + addIndent(indent).append("\"value\":\"" + op.getMaxObjects().getValue().accept(exprVisitor, indent) + "\""); + ILogicalExpression offset = op.getOffset().getValue(); + if (offset != null) { + buffer.append(",\n"); + addIndent(indent).append("\"offset\":\"" + offset.accept(exprVisitor, indent) + "\""); + } + return null; + } + + @Override + public Void visitExchangeOperator(ExchangeOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"exchange\""); + return null; + } + + @Override + public Void visitScriptOperator(ScriptOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"script\""); + if (!op.getInputVariables().isEmpty()) { + addIndent(0).append(",\n"); + addIndent(indent).append("\"in\":["); + boolean first = true; + for (LogicalVariable v : op.getInputVariables()) { + if (!first) { + buffer.append(","); + } + buffer.append("\"" + str(v) + "\""); + first = false; + } + buffer.append("]"); + } + if (!op.getOutputVariables().isEmpty()) { + addIndent(0).append(",\n"); + addIndent(indent).append("\"out\":["); + boolean first = true; + for (LogicalVariable v : op.getOutputVariables()) { + if (!first) { + buffer.append(","); + } + buffer.append("\"" + str(v) + "\""); + first = false; + } + buffer.append("]"); + } + return null; + } + + @Override + public Void visitReplicateOperator(ReplicateOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"replicate\""); + return null; + } + + @Override + public Void visitSplitOperator(SplitOperator op, Integer indent) throws AlgebricksException { + Mutable<ILogicalExpression> branchingExpression = op.getBranchingExpression(); + addIndent(indent).append("\"operator\":\"split\",\n"); + addIndent(indent).append("\"" + branchingExpression.getValue().accept(exprVisitor, indent) + "\""); + return null; + } + + @Override + public Void visitMaterializeOperator(MaterializeOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"materialize\""); + return null; + } + + @Override + public Void visitInsertDeleteUpsertOperator(InsertDeleteUpsertOperator op, Integer indent) + throws AlgebricksException { + String header = "\"operator\":\"" + getIndexOpString(op.getOperation()) + "\",\n"; + addIndent(indent).append(header); + addIndent(indent).append(str("\"data-source\":\"" + op.getDataSource() + "\",\n")); + addIndent(indent).append("\"from-record\":\"") + .append(op.getPayloadExpression().getValue().accept(exprVisitor, indent) + "\""); + if (op.getAdditionalNonFilteringExpressions() != null) { + buffer.append(",\n\"meta\":\""); + pprintExprList(op.getAdditionalNonFilteringExpressions(), 0); + buffer.append("\""); + } + buffer.append(",\n"); + addIndent(indent).append("\"partitioned-by\":{"); + pprintExprList(op.getPrimaryKeyExpressions(), 0); + buffer.append("}"); + if (op.getOperation() == Kind.UPSERT) { + addIndent(indent).append(",\n\"out\":{\n"); + addIndent(indent).append("\"record-before-upsert\":\"" + op.getBeforeOpRecordVar() + "\""); + if (op.getBeforeOpAdditionalNonFilteringVars() != null) { + buffer.append(",\n"); + addIndent(indent) + .append("\"additional-before-upsert\":\"" + op.getBeforeOpAdditionalNonFilteringVars() + "\""); + } + addIndent(indent).append("}"); + } + if (op.isBulkload()) { + buffer.append(",\n"); + addIndent(indent).append("\"bulkload\":\"true\""); + } + return null; + } + + @Override + public Void visitIndexInsertDeleteUpsertOperator(IndexInsertDeleteUpsertOperator op, Integer indent) + throws AlgebricksException { + String header = getIndexOpString(op.getOperation()); + addIndent(indent).append("\"operator\":\"" + header + "\",\n"); + addIndent(indent).append("\"index\":\"" + op.getIndexName() + "\",\n"); + addIndent(indent).append("\"on\":\"").append(str(op.getDataSourceIndex().getDataSource()) + "\",\n"); + addIndent(indent).append("\"from\":{"); + + if (op.getOperation() == Kind.UPSERT) { + + addIndent(indent).append("[\"replace\":\""); + pprintExprList(op.getPrevSecondaryKeyExprs(), 0); + buffer.append("\",\n"); + addIndent(indent).append("\"with\":\""); + pprintExprList(op.getSecondaryKeyExpressions(), 0); + buffer.append("\"}"); + } else { + pprintExprList(op.getSecondaryKeyExpressions(), 0); + } + buffer.append("\n"); + addIndent(indent).append("}"); + if (op.isBulkload()) { + buffer.append(",\n"); + buffer.append("\"bulkload\":\"true\""); + } + return null; + } + + public String getIndexOpString(Kind opKind) { + switch (opKind) { + case DELETE: + return "delete-from"; + case INSERT: + return "insert-into"; + case UPSERT: + return "upsert-into"; + } + return null; + } + + @Override + public Void visitTokenizeOperator(TokenizeOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"tokenize\""); + variablePrintHelper(op.getTokenizeVars(), indent); + if (!op.getSecondaryKeyExpressions().isEmpty()) { + addIndent(0).append(",\n"); + pprintExprList(op.getSecondaryKeyExpressions(), indent); + } + return null; + } + + @Override + public Void visitSinkOperator(SinkOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"sink\""); + return null; + } + + @Override + public Void visitDelegateOperator(DelegateOperator op, Integer indent) throws AlgebricksException { + addIndent(indent).append("\"operator\":\"" + op.toString() + "\""); + return null; + } + + protected AlgebricksAppendable addIndent(int level) throws AlgebricksException { + for (int i = 0; i < level; ++i) { + buffer.append(' '); + } + return buffer; + } + + protected void printNestedPlans(AbstractOperatorWithNestedPlans op, Integer indent) throws AlgebricksException { + idCounter.nextPrefix(); + buffer.append("[\n"); + boolean first = true; + for (ILogicalPlan p : op.getNestedPlans()) { + if (!first) { + buffer.append(","); + } + printPlanJson(p, this, indent + 4); + first = false; + + } + addIndent(indent).append("]"); + idCounter.previousPrefix(); + } + + //Done--Look for exprRef + protected void pprintExprList(List<Mutable<ILogicalExpression>> expressions, Integer indent) + throws AlgebricksException { + addIndent(indent); + buffer.append("\"expressions\":\""); + boolean first = true; + for (Mutable<ILogicalExpression> exprRef : expressions) { + if (first) { + first = false; + } else { + buffer.append(", "); + } + buffer.append(exprRef.getValue().accept(exprVisitor, indent).replace('"', ' ')); + } + buffer.append("\""); + } + + protected void pprintVeList(List<Pair<LogicalVariable, Mutable<ILogicalExpression>>> vePairList, Integer indent) + throws AlgebricksException { + buffer.append("["); + boolean fst = true; + for (Pair<LogicalVariable, Mutable<ILogicalExpression>> ve : vePairList) { + if (fst) { + fst = false; + } else { + buffer.append(","); + } + if (ve.first != null) { + buffer.append("{\"variable\":\"" + ve.first.toString().replace('"', ' ') + "\"," + "\"expression\":\"" + + ve.second.toString().replace('"', ' ') + "\"}"); + } else { + buffer.append("{\"expression\":\"" + ve.second.getValue().accept(exprVisitor, indent).replace('"', ' ') + + "\"}"); + } + } + buffer.append("]"); + } + +} diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/PlanPrettyPrinter.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/PlanPrettyPrinter.java index 9a55f11..cf99d3b 100644 --- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/PlanPrettyPrinter.java +++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/PlanPrettyPrinter.java @@ -18,76 +18,42 @@ */ package org.apache.hyracks.algebricks.core.algebra.prettyprint; -import org.apache.commons.lang3.mutable.Mutable; import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; -import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator; import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan; -import org.apache.hyracks.algebricks.core.algebra.base.IPhysicalOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator; -import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractOperatorWithNestedPlans; public class PlanPrettyPrinter { - public static void printPlan(ILogicalPlan plan, LogicalOperatorPrettyPrintVisitor pvisitor, int indent) - throws AlgebricksException { - for (Mutable<ILogicalOperator> root : plan.getRoots()) { - printOperator((AbstractLogicalOperator) root.getValue(), pvisitor, indent); + @FunctionalInterface + public interface print<T1, T2, T3> { + void apply(T1 arg1, T2 arg2, T3 arg3) throws AlgebricksException; + } + + public static void printOperator(AbstractLogicalOperator op, LogicalOperatorPrettyPrintVisitor pvisitor, + int indent) throws AlgebricksException { + print<AbstractLogicalOperator, LogicalOperatorPrettyPrintVisitor, Integer> printOperator = + LogicalOperatorPrettyPrintVisitor::printOperator; + printOperator.apply(op, pvisitor, indent); + } + + public static <T extends AbstractLogicalOperatorPrettyPrintVisitor> void printPlan(ILogicalPlan plan, + T pvisitor, int indent) throws AlgebricksException { + if (pvisitor.getClass().equals(LogicalOperatorPrettyPrintVisitor.class)) { + print<ILogicalPlan, LogicalOperatorPrettyPrintVisitor, Integer> printPlan = + LogicalOperatorPrettyPrintVisitor::printPlan; + printPlan.apply(plan,(LogicalOperatorPrettyPrintVisitor) pvisitor, indent); } + else if (pvisitor.getClass().equals(LogicalOperatorPrettyPrintVisitorJson.class)) { + print<ILogicalPlan, LogicalOperatorPrettyPrintVisitorJson, Integer> printPlan = + LogicalOperatorPrettyPrintVisitorJson::printPlanJson; + printPlan.apply(plan, (LogicalOperatorPrettyPrintVisitorJson)pvisitor, indent); + } + } public static void printPhysicalOps(ILogicalPlan plan, AlgebricksAppendable out, int indent) throws AlgebricksException { - for (Mutable<ILogicalOperator> root : plan.getRoots()) { - printPhysicalOperator((AbstractLogicalOperator) root.getValue(), indent, out); - } - } - - public static void printOperator(AbstractLogicalOperator op, LogicalOperatorPrettyPrintVisitor pvisitor, int indent) - throws AlgebricksException { - final AlgebricksAppendable out = pvisitor.get(); - op.accept(pvisitor, indent); - IPhysicalOperator pOp = op.getPhysicalOperator(); - - if (pOp != null) { - out.append("\n"); - pad(out, indent); - appendln(out, "-- " + pOp.toString() + " |" + op.getExecutionMode() + "|"); - } else { - appendln(out, " -- |" + op.getExecutionMode() + "|"); - } - - for (Mutable<ILogicalOperator> i : op.getInputs()) { - printOperator((AbstractLogicalOperator) i.getValue(), pvisitor, indent + 2); - } - } - - private static void printPhysicalOperator(AbstractLogicalOperator op, int indent, AlgebricksAppendable out) - throws AlgebricksException { - IPhysicalOperator pOp = op.getPhysicalOperator(); - pad(out, indent); - appendln(out, "-- " + pOp.toString() + " |" + op.getExecutionMode() + "|"); - if (op.hasNestedPlans()) { - AbstractOperatorWithNestedPlans opNest = (AbstractOperatorWithNestedPlans) op; - for (ILogicalPlan p : opNest.getNestedPlans()) { - pad(out, indent + 8); - appendln(out, "{"); - printPhysicalOps(p, out, indent + 10); - pad(out, indent + 8); - appendln(out, "}"); - } - } - for (Mutable<ILogicalOperator> i : op.getInputs()) { - printPhysicalOperator((AbstractLogicalOperator) i.getValue(), indent + 2, out); - } - } - - private static void appendln(AlgebricksAppendable buf, String s) throws AlgebricksException { - buf.append(s); - buf.append("\n"); - } - - private static void pad(AlgebricksAppendable buf, int indent) throws AlgebricksException { - for (int i = 0; i < indent; ++i) { - buf.append(' '); - } + print<ILogicalPlan, AlgebricksAppendable, Integer> printOperator = + AbstractLogicalOperatorPrettyPrintVisitor::printPhysicalOps; + printOperator.apply(plan, out, indent); } } diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/visitors/ILogicalOperatorVisitor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/visitors/ILogicalOperatorVisitor.java index 8da41e2..deb98b0 100644 --- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/visitors/ILogicalOperatorVisitor.java +++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/visitors/ILogicalOperatorVisitor.java @@ -22,11 +22,11 @@ import org.apache.hyracks.algebricks.core.algebra.operators.logical.AggregateOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.AssignOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.DataSourceScanOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.DelegateOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.DistinctOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.DistributeResultOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.EmptyTupleSourceOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.ExchangeOperator; -import org.apache.hyracks.algebricks.core.algebra.operators.logical.DelegateOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.GroupByOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.IndexInsertDeleteUpsertOperator; import org.apache.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator; -- To view, visit https://asterix-gerrit.ics.uci.edu/1885 To unsubscribe, visit https://asterix-gerrit.ics.uci.edu/settings Gerrit-MessageType: merged Gerrit-Change-Id: I4dd62e355048a5b8a02e074049fe41e73e74e357 Gerrit-PatchSet: 30 Gerrit-Project: asterixdb Gerrit-Branch: master Gerrit-Owner: [email protected] Gerrit-Reviewer: Anon. E. Moose #1000171 Gerrit-Reviewer: Ian Maxon <[email protected]> Gerrit-Reviewer: Jenkins <[email protected]> Gerrit-Reviewer: Shiva Jahangiri <[email protected]> Gerrit-Reviewer: Till Westmann <[email protected]>
