From 246646bae5ad076062f7423afe2f6b2d6ea9c874 Mon Sep 17 00:00:00 2001 From: Tobias Weber Date: Thu, 2 May 2024 18:07:50 +0200 Subject: [PATCH] Create PolyAlgRequest for executing PolyAlg via websocket --- .../java/org/polypheny/db/webui/Crud.java | 104 ++++++++++++++++++ .../org/polypheny/db/webui/WebSocket.java | 24 ++++ .../webui/models/requests/PolyAlgRequest.java | 27 +++++ 3 files changed, 155 insertions(+) create mode 100644 webui/src/main/java/org/polypheny/db/webui/models/requests/PolyAlgRequest.java diff --git a/webui/src/main/java/org/polypheny/db/webui/Crud.java b/webui/src/main/java/org/polypheny/db/webui/Crud.java index fcf0fb99bb..f1c31575f1 100644 --- a/webui/src/main/java/org/polypheny/db/webui/Crud.java +++ b/webui/src/main/java/org/polypheny/db/webui/Crud.java @@ -94,6 +94,9 @@ import org.polypheny.db.algebra.constant.Kind; import org.polypheny.db.algebra.core.Sort; import org.polypheny.db.algebra.polyalg.PolyAlgRegistry; +import org.polypheny.db.algebra.polyalg.parser.PolyAlgParser; +import org.polypheny.db.algebra.polyalg.parser.PolyAlgToAlgConverter; +import org.polypheny.db.algebra.polyalg.parser.nodes.PolyAlgNode; import org.polypheny.db.algebra.type.AlgDataType; import org.polypheny.db.algebra.type.AlgDataTypeField; import org.polypheny.db.catalog.Catalog; @@ -151,11 +154,13 @@ import org.polypheny.db.partition.PartitionManager; import org.polypheny.db.partition.PartitionManagerFactory; import org.polypheny.db.partition.properties.PartitionProperty; +import org.polypheny.db.plan.AlgCluster; import org.polypheny.db.plugins.PolyPluginManager; import org.polypheny.db.plugins.PolyPluginManager.PluginStatus; import org.polypheny.db.processing.ImplementationContext; import org.polypheny.db.processing.ImplementationContext.ExecutedContext; import org.polypheny.db.processing.QueryContext; +import org.polypheny.db.rex.RexBuilder; import org.polypheny.db.security.SecurityManager; import org.polypheny.db.transaction.Statement; import org.polypheny.db.transaction.Transaction; @@ -208,6 +213,7 @@ import org.polypheny.db.webui.models.requests.EditTableRequest; import org.polypheny.db.webui.models.requests.PartitioningRequest; import org.polypheny.db.webui.models.requests.PartitioningRequest.ModifyPartitionRequest; +import org.polypheny.db.webui.models.requests.PolyAlgRequest; import org.polypheny.db.webui.models.requests.UIRequest; import org.polypheny.db.webui.models.results.RelationalResult; import org.polypheny.db.webui.models.results.RelationalResult.RelationalResultBuilder; @@ -2382,6 +2388,104 @@ private TimeUnit getFreshnessType( String freshnessId ) { } + /** + * Execute a logical plan represented as PolyAlgebra from the Web-Ui PolyPlan builder + */ + public Result executePolyAlg( PolyAlgRequest polyAlgRequest, Session session ) { + Transaction transaction = getTransaction( true, true, this ); + transaction.getQueryAnalyzer().setSession( session ); + + Statement statement = transaction.createStatement(); + long executionTime = 0; + long temp = 0; + + InformationManager queryAnalyzer = transaction.getQueryAnalyzer().observe( this ); + + AlgRoot root; + try { + temp = System.nanoTime(); + + Snapshot snapshot = statement.getTransaction().getSnapshot(); + RexBuilder rexBuilder = new RexBuilder( statement.getTransaction().getTypeFactory() ); + AlgCluster cluster = AlgCluster.create( statement.getQueryProcessor().getPlanner(), rexBuilder, null, snapshot ); + PolyAlgToAlgConverter converter = new PolyAlgToAlgConverter( snapshot, cluster ); + + PolyAlgParser parser = PolyAlgParser.create( polyAlgRequest.polyAlg ); + PolyAlgNode node = (PolyAlgNode) parser.parseQuery(); + root = converter.convert( node ); + + } catch ( Exception e ) { + log.error( "Caught exception while building the plan builder tree", e ); + return RelationalResult.builder().error( e.getMessage() ).build(); + } + + // TODO: create separate method to reduce duplicate code with executeAlg + // Prepare + PolyImplementation polyImplementation = statement.getQueryProcessor().prepareQuery( root, true ); + + List> rows; + try { + ResultIterator iterator = polyImplementation.execute( statement, getPageSize() ); + rows = iterator.getNextBatch(); + iterator.close(); + } catch ( Exception e ) { + log.error( "Caught exception while iterating the plan builder tree", e ); + return RelationalResult.builder().error( e.getMessage() ).build(); + } + + UiColumnDefinition[] header = new UiColumnDefinition[polyImplementation.getTupleType().getFieldCount()]; + int counter = 0; + for ( AlgDataTypeField col : polyImplementation.getTupleType().getFields() ) { + header[counter++] = UiColumnDefinition.builder() + .name( col.getName() ) + .dataType( col.getType().getFullTypeString() ) + .nullable( col.getType().isNullable() ) + .precision( col.getType().getPrecision() ).build(); + } + + List data = LanguageCrud.computeResultData( rows, List.of( header ), statement.getTransaction() ); + + try { + executionTime += System.nanoTime() - temp; + transaction.commit(); + } catch ( TransactionException e ) { + log.error( "Caught exception while committing the plan builder tree", e ); + try { + transaction.rollback(); + } catch ( TransactionException transactionException ) { + log.error( "Exception while rollback", transactionException ); + } + throw new GenericRuntimeException( e ); + } + RelationalResult finalResult = RelationalResult.builder() + .header( header ) + .data( data.toArray( new String[0][] ) ) + .xid( transaction.getXid().toString() ) + .query( "Execute logical query plan" ) + .build(); + + if ( queryAnalyzer != null ) { + InformationPage p1 = new InformationPage( "Query analysis", "Analysis of the query." ); + InformationGroup g1 = new InformationGroup( p1, "Execution time" ); + InformationText text; + if ( executionTime < 1e4 ) { + text = new InformationText( g1, String.format( "Execution time: %d nanoseconds", executionTime ) ); + } else { + long millis = TimeUnit.MILLISECONDS.convert( executionTime, TimeUnit.NANOSECONDS ); + // format time: see: https://stackoverflow.com/questions/625433/how-to-convert-milliseconds-to-x-mins-x-seconds-in-java#answer-625444 + DateFormat df = new SimpleDateFormat( "m 'min' s 'sec' S 'ms'" ); + String durationText = df.format( new Date( millis ) ); + text = new InformationText( g1, String.format( "Execution time: %s", durationText ) ); + } + queryAnalyzer.addPage( p1 ); + queryAnalyzer.addGroup( g1 ); + queryAnalyzer.registerInformation( text ); + } + + return finalResult; + } + + /** * Execute a logical plan coming from the Web-Ui plan builder */ diff --git a/webui/src/main/java/org/polypheny/db/webui/WebSocket.java b/webui/src/main/java/org/polypheny/db/webui/WebSocket.java index bc0021491d..d90c2683df 100644 --- a/webui/src/main/java/org/polypheny/db/webui/WebSocket.java +++ b/webui/src/main/java/org/polypheny/db/webui/WebSocket.java @@ -42,6 +42,7 @@ import org.polypheny.db.webui.crud.LanguageCrud; import org.polypheny.db.webui.models.requests.AlgRequest; import org.polypheny.db.webui.models.requests.GraphRequest; +import org.polypheny.db.webui.models.requests.PolyAlgRequest; import org.polypheny.db.webui.models.requests.QueryRequest; import org.polypheny.db.webui.models.requests.RegisterRequest; import org.polypheny.db.webui.models.requests.RequestModel; @@ -141,6 +142,29 @@ public void onMessage( final WsMessageContext ctx ) { } ctx.send( results ); break; + case "PolyAlgRequest": + PolyAlgRequest polyAlgRequest = ctx.messageAsClass( PolyAlgRequest.class ); + if ( polyAlgRequest.runQuery ) { + System.out.println("Running query " + polyAlgRequest.polyAlg); + + Result result = null; + try { + result = crud.executePolyAlg( polyAlgRequest, ctx.session ); + } catch ( Throwable t ) { + ctx.send( RelationalResult.builder().error( t.getMessage() ).build() ); + return; + } + + if ( result.xid != null ) { + xIds.add( result.xid ); + } + ctx.send( result ); + + } else { + System.out.println("Building tree for query " + polyAlgRequest.polyAlg); + } + break; + case "RegisterRequest": RegisterRequest registerRequest = ctx.messageAsClass( RegisterRequest.class ); crud.authCrud.register( registerRequest, ctx ); diff --git a/webui/src/main/java/org/polypheny/db/webui/models/requests/PolyAlgRequest.java b/webui/src/main/java/org/polypheny/db/webui/models/requests/PolyAlgRequest.java new file mode 100644 index 0000000000..018548d146 --- /dev/null +++ b/webui/src/main/java/org/polypheny/db/webui/models/requests/PolyAlgRequest.java @@ -0,0 +1,27 @@ +/* + * Copyright 2019-2024 The Polypheny Project + * + * Licensed 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.polypheny.db.webui.models.requests; + +import lombok.Value; + +@Value +public class PolyAlgRequest { + public String type; + public String polyAlg; + public boolean runQuery; // false if the parsed tree should only be generated, but not executed + +}