diff --git a/core/src/main/java/org/polypheny/db/algebra/logical/lpg/LogicalLpgValues.java b/core/src/main/java/org/polypheny/db/algebra/logical/lpg/LogicalLpgValues.java index 481cee9b96..cd35b8d181 100644 --- a/core/src/main/java/org/polypheny/db/algebra/logical/lpg/LogicalLpgValues.java +++ b/core/src/main/java/org/polypheny/db/algebra/logical/lpg/LogicalLpgValues.java @@ -191,8 +191,8 @@ private ImmutableList> getEdgeValues( ImmutableList>> enumerables, int i, AlgDataType idType, AlgDataType labelType, PolyEdge edge ) { context.addParameterValues( 0, idType, Collections.nCopies( edge.labels.size(), edge.id ) ); context.addParameterValues( 1, labelType, List.of( edge.labels.get( 0 ) ) ); - context.addParameterValues( 2, idType, List.of( edge.source ) ); - context.addParameterValues( 3, idType, List.of( edge.target ) ); + context.addParameterValues( 2, idType, List.of( edge.left ) ); + context.addParameterValues( 3, idType, List.of( edge.right ) ); // execute all inserts drainInserts( enumerables.get( i ), edge.labels.size() ); diff --git a/core/src/main/java/org/polypheny/db/type/entity/graph/PolyEdge.java b/core/src/main/java/org/polypheny/db/type/entity/graph/PolyEdge.java index b38579fad0..78eb860fb1 100644 --- a/core/src/main/java/org/polypheny/db/type/entity/graph/PolyEdge.java +++ b/core/src/main/java/org/polypheny/db/type/entity/graph/PolyEdge.java @@ -51,9 +51,9 @@ public class PolyEdge extends GraphPropertyHolder { @JsonProperty - public PolyString source; + public PolyString left; @JsonProperty - public PolyString target; + public PolyString right; @JsonProperty public EdgeDirection direction; @@ -67,11 +67,11 @@ public class PolyEdge extends GraphPropertyHolder { public PolyEdge( @NonNull PolyDictionary properties, List labels, - PolyString source, - PolyString target, + PolyString left, + PolyString right, EdgeDirection direction, PolyString variableName ) { - this( PolyString.of( UUID.randomUUID().toString() ), properties, labels, source, target, direction, variableName ); + this( PolyString.of( UUID.randomUUID().toString() ), properties, labels, left, right, direction, variableName ); } @@ -79,13 +79,13 @@ public PolyEdge( @JsonProperty("id") PolyString id, @JsonProperty("properties") @NonNull PolyDictionary properties, @JsonProperty("labels") List labels, - @JsonProperty("source") PolyString source, - @JsonProperty("target") PolyString target, + @JsonProperty("source") PolyString left, + @JsonProperty("target") PolyString right, @JsonProperty("direction") EdgeDirection direction, @JsonProperty("variableName") PolyString variableName ) { super( id, PolyType.EDGE, properties, labels, variableName ); - this.source = source; - this.target = target; + this.left = left; + this.right = right; this.direction = direction; } @@ -99,7 +99,7 @@ public int getVariants() { public PolyEdge from( PolyString left, PolyString right ) { - return new PolyEdge( id, properties, labels, left == null ? this.source : left, right == null ? this.target : right, direction, null ); + return new PolyEdge( id, properties, labels, left == null ? this.left : left, right == null ? this.right : right, direction, null ); } @@ -120,7 +120,7 @@ public PolyEdge copyNamed( PolyString newName ) { // no copy needed return this; } - return new PolyEdge( id, properties, labels, source, target, direction, newName ); + return new PolyEdge( id, properties, labels, left, right, direction, newName ); } @@ -182,8 +182,8 @@ public Expression asExpression() { id.asExpression(), properties.asExpression(), labels.asExpression(), - source.asExpression(), - target.asExpression(), + left.asExpression(), + right.asExpression(), Expressions.constant( direction ), getVariableName() == null ? Expressions.constant( null ) : getVariableName().asExpression() ), PolyEdge.class ); @@ -200,8 +200,8 @@ public String toJson() { return "{\"id\":" + id.toQuotedJson() + ", \"properties\":" + properties.toJson() + ", \"labels\":" + labels.toJson() + - ", \"source\":" + source.toQuotedJson() + - ", \"target\":" + target.toQuotedJson() + + ", \"source\":" + left.toQuotedJson() + + ", \"target\":" + right.toQuotedJson() + ", \"direction\":\"" + direction.name() + "\"" + "}"; } @@ -238,8 +238,8 @@ public String toString() { "id=" + id + ", properties=" + properties + ", labels=" + labels + - ", leftId=" + source + - ", rightId=" + target + + ", leftId=" + left + + ", rightId=" + right + ", direction=" + direction + '}'; } @@ -259,8 +259,8 @@ public void encode( BinaryOutput out, PolyEdge item ) { out.writeUTF8Nullable( item.variableName.value ); } out.writeUTF8( item.direction.name() ); - out.writeUTF8( item.source.value ); - out.writeUTF8( item.target.value ); + out.writeUTF8( item.left.value ); + out.writeUTF8( item.right.value ); out.writeUTF8( item.properties.serialize() ); } diff --git a/core/src/main/java/org/polypheny/db/type/entity/graph/PolyGraph.java b/core/src/main/java/org/polypheny/db/type/entity/graph/PolyGraph.java index 643674a40c..0f1731c870 100644 --- a/core/src/main/java/org/polypheny/db/type/entity/graph/PolyGraph.java +++ b/core/src/main/java/org/polypheny/db/type/entity/graph/PolyGraph.java @@ -208,24 +208,24 @@ private List buildMatchingTree( List segments ) { // for one only edges, which have matching nodes are considered, // additionally only not used edges can be use, relationship isomorphism prohibits this BiPredicate filter = - segment.direction == EdgeDirection.LEFT_TO_RIGHT ? (( e, p ) -> !p.usedEdgesIds.contains( e.id ) && e.source.equals( p.targetId )) : - segment.direction == EdgeDirection.RIGHT_TO_LEFT ? (( e, p ) -> !p.usedEdgesIds.contains( e.id ) && e.target.equals( p.targetId )) : - (( e, p ) -> !p.usedEdgesIds.contains( e.id ) && (e.target.equals( p.targetId ) || e.source.equals( p.targetId ))); + segment.direction == EdgeDirection.LEFT_TO_RIGHT ? (( e, p ) -> !p.usedEdgesIds.contains( e.id ) && e.left.equals( p.targetId )) : + segment.direction == EdgeDirection.RIGHT_TO_LEFT ? (( e, p ) -> !p.usedEdgesIds.contains( e.id ) && e.right.equals( p.targetId )) : + (( e, p ) -> !p.usedEdgesIds.contains( e.id ) && (e.right.equals( p.targetId ) || e.left.equals( p.targetId ))); for ( TreePart part : last ) { // only loop matching connections for ( PolyEdge edge : edges.values().stream().filter( e -> filter.test( e, part ) ).toList() ) { - PolyNode left = nodes.get( edge.source ); - PolyNode right = nodes.get( edge.target ); + PolyNode left = nodes.get( edge.left ); + PolyNode right = nodes.get( edge.right ); // then check if it matches pattern of segment either ()->() or ()-() depending if direction is specified if ( segment.direction == EdgeDirection.LEFT_TO_RIGHT || segment.direction == EdgeDirection.NONE ) { - if ( segment.matches( left, edge, right ) && !part.usedEdgesIds.contains( edge.id ) && part.targetId.equals( edge.source ) ) { - matches.add( new TreePart( part, edge.id, edge.target, segment.edge.getVariableName(), segment.target.getVariableName() ) ); + if ( segment.matches( left, edge, right ) && !part.usedEdgesIds.contains( edge.id ) && part.targetId.equals( edge.left ) ) { + matches.add( new TreePart( part, edge.id, edge.right, segment.edge.getVariableName(), segment.target.getVariableName() ) ); } } if ( segment.direction == EdgeDirection.RIGHT_TO_LEFT || segment.direction == EdgeDirection.NONE ) { - if ( segment.matches( right, edge, left ) && !part.usedEdgesIds.contains( edge.id ) && part.targetId.equals( edge.target ) ) { - matches.add( new TreePart( part, edge.id, edge.source, segment.edge.getVariableName(), segment.target.getVariableName() ) ); + if ( segment.matches( right, edge, left ) && !part.usedEdgesIds.contains( edge.id ) && part.targetId.equals( edge.right ) ) { + matches.add( new TreePart( part, edge.id, edge.left, segment.edge.getVariableName(), segment.target.getVariableName() ) ); } } @@ -248,18 +248,18 @@ private List buildMatchingTree( List segments ) { private void attachEmptyStubs( PolySegment segment, List root ) { Set> usedIds = new HashSet<>(); for ( PolyEdge edge : edges.values() ) { - PolyNode left = nodes.get( edge.source ); - PolyNode right = nodes.get( edge.target ); + PolyNode left = nodes.get( edge.left ); + PolyNode right = nodes.get( edge.right ); // We attach stubs, which allows ()->() and ()-() if ( segment.direction == EdgeDirection.LEFT_TO_RIGHT || segment.direction == EdgeDirection.NONE ) { if ( segment.matches( left, edge, right ) ) { - usedIds.add( Pair.of( edge.source, segment.source.getVariableName() ) ); + usedIds.add( Pair.of( edge.left, segment.source.getVariableName() ) ); } } // We attach stubs, which allows ()<-() and ()-() AKA inverted if ( segment.direction == EdgeDirection.RIGHT_TO_LEFT || segment.direction == EdgeDirection.NONE ) { if ( segment.matches( right, edge, left ) ) { - usedIds.add( Pair.of( edge.target, segment.source.getVariableName() ) ); + usedIds.add( Pair.of( edge.right, segment.source.getVariableName() ) ); } } } diff --git a/dbms/src/test/java/org/polypheny/db/prisminterface/CypherTest.java b/dbms/src/test/java/org/polypheny/db/prisminterface/CypherTest.java new file mode 100644 index 0000000000..a260bc3c46 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/prisminterface/CypherTest.java @@ -0,0 +1,182 @@ +/* + * 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.prisminterface; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Iterator; +import java.util.List; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.polypheny.db.TestHelper.JdbcConnection; +import org.polypheny.jdbc.PolyConnection; +import org.polypheny.jdbc.multimodel.GraphResult; +import org.polypheny.jdbc.multimodel.PolyRow; +import org.polypheny.jdbc.multimodel.PolyStatement; +import org.polypheny.jdbc.multimodel.RelationalResult; +import org.polypheny.jdbc.types.PolyEdge; +import org.polypheny.jdbc.types.PolyGraphElement; +import org.polypheny.jdbc.types.PolyNode; + +public class CypherTest { + + @BeforeAll + public static void setup() throws SQLException { + try ( Connection connection = new JdbcConnection( true ).getConnection() ) { + if ( !connection.isWrapperFor( PolyConnection.class ) ) { + fail( "Driver must support unwrapping to PolyConnection" ); + } + PolyStatement polyStatement = connection.unwrap( PolyConnection.class ).createPolyStatement(); + polyStatement.execute( "public", "sql", "DROP NAMESPACE IF EXISTS cyphertest" ); + polyStatement.execute( "public", "sql", "CREATE GRAPH NAMESPACE cyphertest" ); + polyStatement.execute( "cyphertest", "cypher", "CREATE (:Person {id: 1, name: 'Alice'})" ); + polyStatement.execute( "cyphertest", "cypher", "CREATE (:Person {id: 2, name: 'Bob'})" ); + polyStatement.execute( "cyphertest", "cypher", "CREATE (:Person {id: 3, name: 'Charlie'})" ); + polyStatement.execute( "cyphertest", "cypher", "MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'}) CREATE (a)-[:KNOWS]->(b)" ); + polyStatement.execute( "cyphertest", "cypher", "MATCH (b:Person {name: 'Bob'}), (c:Person {name: 'Charlie'}) CREATE (b)-[:KNOWS]->(c)" ); + } + } + + + @AfterAll + public static void teardown() throws SQLException { + try ( Connection connection = new JdbcConnection( true ).getConnection() ) { + if ( !connection.isWrapperFor( PolyConnection.class ) ) { + fail( "Driver must support unwrapping to PolyConnection" ); + } + PolyStatement polyStatement = connection.unwrap( PolyConnection.class ).createPolyStatement(); + polyStatement.execute( "public", "sql", "DROP NAMESPACE IF EXISTS cyphertest" ); + } + } + + + @Test + public void cypherSelectNodesTest() throws SQLException { + try ( Connection connection = new JdbcConnection( true ).getConnection() ) { + if ( !connection.isWrapperFor( PolyConnection.class ) ) { + fail( "Driver must support unwrapping to PolyConnection" ); + } + PolyStatement polyStatement = connection.unwrap( PolyConnection.class ).createPolyStatement(); + GraphResult result = polyStatement.execute( "cyphertest", "cypher", "MATCH (n:Person) RETURN n ORDER BY n.id" ).unwrap( GraphResult.class ); + Iterator elements = result.iterator(); + + assertTrue( elements.hasNext() ); + PolyGraphElement element = elements.next(); + assertEquals( "Alice", element.get( "name" ).asString() ); + assertEquals( 1, element.getLabels().size() ); + assertEquals( "Person", element.getLabels().get( 0 ) ); + + assertTrue( elements.hasNext() ); + element = elements.next(); + assertEquals( "Bob", element.get( "name" ).asString() ); + assertEquals( 1, element.getLabels().size() ); + assertEquals( "Person", element.getLabels().get( 0 ) ); + + assertTrue( elements.hasNext() ); + element = elements.next(); + assertEquals( "Charlie", element.get( "name" ).asString() ); + assertEquals( 1, element.getLabels().size() ); + assertEquals( "Person", element.getLabels().get( 0 ) ); + + assertFalse( elements.hasNext() ); + } + } + + + @Test + public void cypherSelectEdgesTest() throws SQLException { + try ( Connection connection = new JdbcConnection( true ).getConnection() ) { + if ( !connection.isWrapperFor( PolyConnection.class ) ) { + fail( "Driver must support unwrapping to PolyConnection" ); + } + PolyStatement polyStatement = connection.unwrap( PolyConnection.class ).createPolyStatement(); + GraphResult result = polyStatement.execute( "cyphertest", "cypher", "MATCH ()-[r:KNOWS]->() RETURN r" ).unwrap( GraphResult.class ); + Iterator elements = result.iterator(); + + assertTrue( elements.hasNext() ); + PolyGraphElement element = elements.next(); + assertEquals( "KNOWS", element.getLabels().get( 0 ) ); + + assertTrue( elements.hasNext() ); + element = elements.next(); + assertEquals( "KNOWS", element.getLabels().get( 0 ) ); + + assertFalse( elements.hasNext() ); + } + } + + + @Test + public void cypherSelectPathsTest() throws SQLException { + try ( Connection connection = new JdbcConnection( true ).getConnection() ) { + if ( !connection.isWrapperFor( PolyConnection.class ) ) { + fail( "Driver must support unwrapping to PolyConnection" ); + } + PolyStatement polyStatement = connection.unwrap( PolyConnection.class ).createPolyStatement(); + GraphResult result = polyStatement.execute( "cyphertest", "cypher", "MATCH p=(:Person {name: 'Alice'})-[:KNOWS*]->(:Person {name: 'Charlie'}) RETURN p" ).unwrap( GraphResult.class ); + Iterator elements = result.iterator(); + + assertTrue( elements.hasNext() ); + PolyNode alice = elements.next().unwrap( PolyNode.class ); + assertEquals( alice.getLabels(), List.of( "Person" ) ); + assertEquals( alice.get( "name" ).asString(), "Alice" ); + + PolyEdge knows = elements.next().unwrap( PolyEdge.class ); + assertEquals( knows.getLabels(), List.of( "KNOWS" ) ); + + PolyNode bob = elements.next().unwrap( PolyNode.class ); + assertEquals( bob.getLabels(), List.of( "Person" ) ); + assertEquals( bob.get( "name" ).asString(), "Bob" ); + + knows = elements.next().unwrap( PolyEdge.class ); + assertEquals( knows.getLabels(), List.of( "KNOWS" ) ); + + PolyNode charlie = elements.next().unwrap( PolyNode.class ); + assertEquals( charlie.getLabels(), List.of( "Person" ) ); + assertEquals( charlie.get( "name" ).asString(), "Charlie" ); + + assertFalse( elements.hasNext() ); + } + } + + + @Test + public void cypherRelationalTest() throws SQLException { + try ( Connection connection = new JdbcConnection( true ).getConnection() ) { + if ( !connection.isWrapperFor( PolyConnection.class ) ) { + fail( "Driver must support unwrapping to PolyConnection" ); + } + PolyStatement polyStatement = connection.unwrap( PolyConnection.class ).createPolyStatement(); + RelationalResult result = polyStatement.execute( "cyphertest", "cypher", "MATCH (n:Person {name: 'Alice'}) RETURN n.name, n.id" ).unwrap( RelationalResult.class ); + Iterator rows = result.iterator(); + + assertTrue( rows.hasNext() ); + PolyRow row = rows.next(); + assertEquals( "Alice", row.get( "n.name" ).asString() ); + assertEquals( "1", row.get( "n.id" ).asString() ); + + assertFalse( rows.hasNext() ); + } + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/prisminterface/MqlTest.java b/dbms/src/test/java/org/polypheny/db/prisminterface/MqlTest.java index 65e87560cc..0265c3c3ef 100644 --- a/dbms/src/test/java/org/polypheny/db/prisminterface/MqlTest.java +++ b/dbms/src/test/java/org/polypheny/db/prisminterface/MqlTest.java @@ -18,52 +18,63 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.sql.Connection; import java.sql.SQLException; -import java.sql.Statement; +import java.util.Iterator; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.polypheny.db.TestHelper.JdbcConnection; import org.polypheny.jdbc.PolyConnection; -import org.polypheny.jdbc.PrismInterfaceServiceException; +import org.polypheny.jdbc.multimodel.DocumentResult; import org.polypheny.jdbc.multimodel.PolyStatement; -import org.polypheny.jdbc.multimodel.Result; -import org.polypheny.jdbc.multimodel.Result.ResultType; +import org.polypheny.jdbc.types.PolyDocument; public class MqlTest { - private static final String MQL_LANGUAGE_NAME = "mongo"; - private static final String TEST_QUERY = "db.students.find();"; - - - @Test - public void connectionUnwrapTest() throws SQLException { + @BeforeAll + public static void setup() throws SQLException { try ( Connection connection = new JdbcConnection( true ).getConnection() ) { if ( !connection.isWrapperFor( PolyConnection.class ) ) { - fail( "Driver must support unwrapping to PolyphenyConnection" ); + fail( "Driver must support unwrapping to PolyConnection" ); } + PolyStatement polyStatement = connection.unwrap( PolyConnection.class ).createPolyStatement(); + + polyStatement.execute( "public", "sql", "DROP NAMESPACE IF EXISTS mongotest" ); + polyStatement.execute( "public", "sql", "CREATE DOCUMENT NAMESPACE mongotest" ); + polyStatement.execute( "mongotest", "mongo", "db.createCollection(test_collection)" ); + polyStatement.execute( "mongotest", "mongo", "db.test_collection.insert({ \"_id\": 1, \"name\": \"Alice\" })" ); + polyStatement.execute( "mongotest", "mongo", "db.test_collection.insert({ \"_id\": 2, \"name\": \"Bob\" })" ); } } @Test - public void simpleMqlTest() throws SQLException { + public void mongoSelectTest() throws SQLException { try ( Connection connection = new JdbcConnection( true ).getConnection() ) { - try ( Statement statement = connection.createStatement() ) { - statement.execute( "DROP NAMESPACE IF EXISTS mqltest" ); - statement.execute( "CREATE DOCUMENT NAMESPACE mqltest" ); - } if ( !connection.isWrapperFor( PolyConnection.class ) ) { - fail( "Driver must support unwrapping to PolyphenyConnection" ); + fail( "Driver must support unwrapping to PolyConnection" ); } PolyStatement polyStatement = connection.unwrap( PolyConnection.class ).createPolyStatement(); - assertThrows( PrismInterfaceServiceException.class, () -> { - // fails due to autogenerated query (would create students collection) - Result result = polyStatement.execute( "mqltest", MQL_LANGUAGE_NAME, TEST_QUERY ); - assertEquals( ResultType.DOCUMENT, result.getResultType() ); - } ); + DocumentResult result = polyStatement.execute( "mongotest", "mongo", "db.test_collection.find({})" ).unwrap( DocumentResult.class ); + Iterator documents = result.iterator(); + + assertTrue( documents.hasNext() ); + PolyDocument document = documents.next(); + + assertEquals( 1, document.get( "_id" ).asInt() ); + assertEquals( "Alice", document.get( "name" ).asString() ); + + assertTrue( documents.hasNext() ); + document = documents.next(); + + assertEquals( 2, document.get( "_id" ).asInt() ); + assertEquals( "Bob", document.get( "name" ).asString() ); + + assertFalse( documents.hasNext() ); } } diff --git a/dbms/src/test/java/org/polypheny/db/prisminterface/SqlTest.java b/dbms/src/test/java/org/polypheny/db/prisminterface/SqlTest.java new file mode 100644 index 0000000000..7b31439e25 --- /dev/null +++ b/dbms/src/test/java/org/polypheny/db/prisminterface/SqlTest.java @@ -0,0 +1,117 @@ +/* + * 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.prisminterface; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Iterator; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.polypheny.db.TestHelper.JdbcConnection; +import org.polypheny.jdbc.PolyConnection; +import org.polypheny.jdbc.multimodel.PolyRow; +import org.polypheny.jdbc.multimodel.PolyStatement; +import org.polypheny.jdbc.multimodel.RelationalColumnMetadata; +import org.polypheny.jdbc.multimodel.RelationalMetadata; +import org.polypheny.jdbc.multimodel.RelationalResult; + +public class SqlTest { + + @BeforeAll + public static void setup() throws SQLException { + try ( Connection connection = new JdbcConnection( true ).getConnection() ) { + if ( !connection.isWrapperFor( PolyConnection.class ) ) { + fail( "Driver must support unwrapping to PolyConnection" ); + } + PolyStatement polyStatement = connection.unwrap( PolyConnection.class ).createPolyStatement(); + polyStatement.execute( "public", "sql", "DROP NAMESPACE IF EXISTS sqltest" ); + polyStatement.execute( "public", "sql", "CREATE RELATIONAL NAMESPACE sqltest" ); + polyStatement.execute( "sqltest", "sql", "CREATE TABLE sqltest.test_table (id INT PRIMARY KEY, name VARCHAR(50))" ); + polyStatement.execute( "sqltest", "sql", "INSERT INTO sqltest.test_table (id, name) VALUES (1, 'Alice')" ); + polyStatement.execute( "sqltest", "sql", "INSERT INTO sqltest.test_table (id, name) VALUES (2, 'Bob')" ); + } + } + + + @Test + public void sqlSelectTest() throws SQLException { + try ( Connection connection = new JdbcConnection( true ).getConnection() ) { + if ( !connection.isWrapperFor( PolyConnection.class ) ) { + fail( "Driver must support unwrapping to PolyConnection" ); + } + PolyStatement polyStatement = connection.unwrap( PolyConnection.class ).createPolyStatement(); + RelationalResult result = polyStatement.execute( "sqltest", "sql", "SELECT * FROM test_table" ).unwrap( RelationalResult.class ); + Iterator rows = result.unwrap( RelationalResult.class ).iterator(); + assertTrue( rows.hasNext() ); + PolyRow row = rows.next(); + + assertEquals( 1, row.get( "id" ).asInt() ); + assertEquals( "Alice", row.get( "name" ).asString() ); + + assertEquals( 1, row.get( 0 ).asInt() ); + assertEquals( "Alice", row.get( 1 ).asString() ); + + assertTrue( rows.hasNext() ); + row = rows.next(); + + assertEquals( 2, row.get( "id" ).asInt() ); + assertEquals( "Bob", row.get( "name" ).asString() ); + + assertEquals( 2, row.get( 0 ).asInt() ); + assertEquals( "Bob", row.get( 1 ).asString() ); + } + } + + + @Test + public void sqlSelectMetadataTest() throws SQLException { + try ( Connection connection = new JdbcConnection( true ).getConnection() ) { + if ( !connection.isWrapperFor( PolyConnection.class ) ) { + fail( "Driver must support unwrapping to PolyConnection" ); + } + PolyStatement polyStatement = connection.unwrap( PolyConnection.class ).createPolyStatement(); + RelationalMetadata relationalMetadata = polyStatement.execute( "sqltest", "sql", "SELECT * FROM test_table" ).unwrap( RelationalResult.class ).getMetadata(); + assertEquals( 2, relationalMetadata.getColumnCount() ); + + RelationalColumnMetadata relationalColumnMetadata1 = relationalMetadata.getColumnMeta( 0 ); + assertEquals( 0, relationalColumnMetadata1.getColumnIndex() ); + assertFalse( relationalColumnMetadata1.isNullable() ); // false as this is the primary key + assertEquals( 10, relationalColumnMetadata1.getLength() ); + assertEquals( "id", relationalColumnMetadata1.getColumnLabel() ); + assertEquals( "id", relationalColumnMetadata1.getColumnName() ); + assertEquals( 10, relationalColumnMetadata1.getPrecision() ); + assertEquals( "INTEGER", relationalColumnMetadata1.getProtocolTypeName() ); + assertEquals( 0, relationalColumnMetadata1.getScale() ); + + RelationalColumnMetadata relationalColumnMetadata2 = relationalMetadata.getColumnMeta( 1 ); + assertEquals( 1, relationalColumnMetadata2.getColumnIndex() ); + assertTrue( relationalColumnMetadata2.isNullable() ); + assertEquals( 50, relationalColumnMetadata2.getLength() ); + assertEquals( "name", relationalColumnMetadata2.getColumnLabel() ); + assertEquals( "name", relationalColumnMetadata2.getColumnName() ); + assertEquals( 50, relationalColumnMetadata2.getPrecision() ); + assertEquals( "VARCHAR", relationalColumnMetadata2.getProtocolTypeName() ); + assertEquals( -2147483648, relationalColumnMetadata2.getScale() ); + } + } + +} diff --git a/gradle.properties b/gradle.properties index 625af03536..470f57a594 100644 --- a/gradle.properties +++ b/gradle.properties @@ -80,13 +80,13 @@ opencsv_version = 5.9 oshi_core_version = 6.6.2 oauth_client_version = 1.34.1 poi_version = 5.2.3 -polypheny_jdbc_driver_version = 2.2 +polypheny_jdbc_driver_version = 2.4-SNAPSHOT polypheny_ui_version = 2.0-SNAPSHOT postgresql_version = 42.2.19 postgis_version = 2024.1.0 proj4j_version = 1.3.0 pf4j_version = 3.12.0 -prism_api_version = 1.9 +prism_api_version = 1.10 protobuf_version = 3.23.4 protobuf_plugin_version = 0.9.4 reflections_version = 0.10.2 diff --git a/plugins/neo4j-adapter/src/main/java/org/polypheny/db/adapter/neo4j/rules/graph/NeoLpgModify.java b/plugins/neo4j-adapter/src/main/java/org/polypheny/db/adapter/neo4j/rules/graph/NeoLpgModify.java index 4a7fadf863..ca687403e9 100644 --- a/plugins/neo4j-adapter/src/main/java/org/polypheny/db/adapter/neo4j/rules/graph/NeoLpgModify.java +++ b/plugins/neo4j-adapter/src/main/java/org/polypheny/db/adapter/neo4j/rules/graph/NeoLpgModify.java @@ -166,7 +166,7 @@ private List getCreatePath( ImmutableList nodes, Immutab statements.add( node_( node, PolyString.of( implementor.getGraph().mappingLabel ), true ) ); } for ( PolyEdge edge : edges ) { - statements.add( path_( node_( uuidNameMapping.get( edge.source ) ), edge_( edge, true ), node_( uuidNameMapping.get( edge.target ) ) ) ); + statements.add( path_( node_( uuidNameMapping.get( edge.left ) ), edge_( edge, true ), node_( uuidNameMapping.get( edge.right ) ) ) ); } return statements; diff --git a/plugins/neo4j-adapter/src/main/java/org/polypheny/db/adapter/neo4j/util/NeoStatements.java b/plugins/neo4j-adapter/src/main/java/org/polypheny/db/adapter/neo4j/util/NeoStatements.java index 3a08939b23..26898f9e80 100644 --- a/plugins/neo4j-adapter/src/main/java/org/polypheny/db/adapter/neo4j/util/NeoStatements.java +++ b/plugins/neo4j-adapter/src/main/java/org/polypheny/db/adapter/neo4j/util/NeoStatements.java @@ -324,8 +324,8 @@ static EdgeStatement edge_( PolyEdge edge, boolean addId ) { List props = new ArrayList<>( properties_( edge.properties ) ); if ( addId ) { props.add( property_( PolyString.of( "_id" ), string_( edge.id ) ) ); - props.add( property_( PolyString.of( "__sourceId__" ), string_( edge.source ) ) ); - props.add( property_( PolyString.of( "__targetId__" ), string_( edge.target ) ) ); + props.add( property_( PolyString.of( "__sourceId__" ), string_( edge.left ) ) ); + props.add( property_( PolyString.of( "__targetId__" ), string_( edge.right ) ) ); } PolyString defIdentifier = edge.getVariableName(); diff --git a/plugins/prism-interface/build.gradle b/plugins/prism-interface/build.gradle index 244efa1278..537f91f7ff 100644 --- a/plugins/prism-interface/build.gradle +++ b/plugins/prism-interface/build.gradle @@ -16,6 +16,7 @@ configurations { repositories { mavenCentral() + mavenLocal() } dependencies { diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/PIClient.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/PIClient.java index 4e3aba4118..bd9f14d10f 100644 --- a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/PIClient.java +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/PIClient.java @@ -21,6 +21,7 @@ import lombok.extern.slf4j.Slf4j; import org.polypheny.db.catalog.entity.LogicalUser; import org.polypheny.db.catalog.entity.logical.LogicalNamespace; +import org.polypheny.db.prisminterface.metaRetrieval.PIClientInfoProperties; import org.polypheny.db.prisminterface.statements.StatementManager; import org.polypheny.db.transaction.Transaction; import org.polypheny.db.transaction.TransactionException; diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/PIService.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/PIService.java index b591342e5a..0df6e87f5c 100644 --- a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/PIService.java +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/PIService.java @@ -30,6 +30,8 @@ import org.polypheny.db.catalog.exceptions.GenericRuntimeException; import org.polypheny.db.iface.AuthenticationException; import org.polypheny.db.languages.QueryLanguage; +import org.polypheny.db.prisminterface.metaRetrieval.DbMetaRetriever; +import org.polypheny.db.prisminterface.metaRetrieval.PIClientInfoProperties; import org.polypheny.db.prisminterface.statementProcessing.StatementProcessor; import org.polypheny.db.prisminterface.statements.PIPreparedIndexedStatement; import org.polypheny.db.prisminterface.statements.PIPreparedNamedStatement; diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/PIServiceException.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/PIServiceException.java index 91d5744760..eb45a06fac 100644 --- a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/PIServiceException.java +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/PIServiceException.java @@ -17,41 +17,18 @@ package org.polypheny.db.prisminterface; import java.sql.SQLException; -import java.util.Optional; -import lombok.Getter; import org.polypheny.db.catalog.exceptions.GenericRuntimeException; -import org.polypheny.prism.ErrorDetails; public class PIServiceException extends GenericRuntimeException { - @Getter - private String state; - @Getter - private int errorCode; - - public PIServiceException( String reason, String state, int errorCode ) { - super( reason ); - this.state = state; - this.errorCode = errorCode; - } - - - public PIServiceException( String reason, String state ) { + public PIServiceException( String reason ) { super( reason ); - this.state = state; } public PIServiceException( SQLException sqlException ) { super( sqlException.getMessage(), sqlException ); - this.state = sqlException.getSQLState(); - this.errorCode = sqlException.getErrorCode(); - } - - - public PIServiceException( String reason ) { - super( reason ); } @@ -64,33 +41,4 @@ public PIServiceException( String reason, Throwable cause ) { super( reason, cause ); } - - public PIServiceException( String reason, String state, Throwable cause ) { - super( reason, cause ); - this.state = state; - } - - - public PIServiceException( String reason, String state, int errorCode, Throwable cause ) { - super( reason, cause ); - this.state = state; - this.errorCode = errorCode; - } - - - public PIServiceException( ErrorDetails errorDetails ) { - super( errorDetails.hasMessage() ? errorDetails.getMessage() : null ); - this.state = errorDetails.hasState() ? errorDetails.getState() : null; - this.errorCode = errorDetails.hasErrorCode() ? errorDetails.getErrorCode() : 0; - } - - - public ErrorDetails getPrismErrorDetails() { - ErrorDetails.Builder errorDetailsBuilder = ErrorDetails.newBuilder(); - errorDetailsBuilder.setErrorCode( getErrorCode() ); - Optional.ofNullable( getState() ).ifPresent( errorDetailsBuilder::setState ); - Optional.ofNullable( getMessage() ).ifPresent( errorDetailsBuilder::setMessage ); - return errorDetailsBuilder.build(); - } - } diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/DbMetaRetriever.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/metaRetrieval/DbMetaRetriever.java similarity index 95% rename from plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/DbMetaRetriever.java rename to plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/metaRetrieval/DbMetaRetriever.java index 3f7207f6ee..b1e1ae7a66 100644 --- a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/DbMetaRetriever.java +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/metaRetrieval/DbMetaRetriever.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.polypheny.db.prisminterface; +package org.polypheny.db.prisminterface.metaRetrieval; import java.sql.DatabaseMetaData; import java.util.Arrays; @@ -38,6 +38,7 @@ import org.polypheny.db.catalog.logistic.Pattern; import org.polypheny.db.languages.OperatorRegistry; import org.polypheny.db.languages.QueryLanguage; +import org.polypheny.db.prisminterface.PIServiceException; import org.polypheny.db.type.PolyType; import org.polypheny.prism.ClientInfoPropertyMeta; import org.polypheny.prism.ClientInfoPropertyMetaResponse; @@ -60,10 +61,10 @@ import org.polypheny.prism.Type; import org.polypheny.prism.TypesResponse; -class DbMetaRetriever { +public class DbMetaRetriever { // Namespace search by name and type - static NamespacesResponse searchNamespaces( String namespacePattern, String namespaceType ) { + public static NamespacesResponse searchNamespaces( String namespacePattern, String namespaceType ) { List namespaces = getLogicalNamespaces( namespacePattern, namespaceType ); NamespacesResponse.Builder responseBuilder = NamespacesResponse.newBuilder(); namespaces.forEach( namespace -> responseBuilder.addNamespaces( getNamespaceMeta( namespace ) ) ); @@ -101,7 +102,7 @@ public static Namespace getNamespace( String namespaceName ) { // Entity search by namespace - static EntitiesResponse searchEntities( String namespaceName, String entityPattern ) { + public static EntitiesResponse searchEntities( String namespaceName, String entityPattern ) { EntitiesResponse.Builder responseBuilder = EntitiesResponse.newBuilder(); LogicalNamespace namespace = Catalog.snapshot().getNamespace( namespaceName ).orElseThrow(); return switch ( namespace.getDataModel() ) { @@ -249,14 +250,14 @@ static List getGraphEntities( long namespaceId, String entityPattern ) { } - static synchronized DefaultNamespaceResponse getDefaultNamespace() { + public static synchronized DefaultNamespaceResponse getDefaultNamespace() { return DefaultNamespaceResponse.newBuilder() .setDefaultNamespace( Catalog.DEFAULT_NAMESPACE_NAME ) .build(); } - static synchronized TableTypesResponse getTableTypes() { + public static synchronized TableTypesResponse getTableTypes() { List tableTypes = Arrays.stream( EntityType.values() ).map( EntityType::name ).toList(); TableTypesResponse.Builder responseBuilder = TableTypesResponse.newBuilder(); tableTypes.forEach( tableType -> responseBuilder.addTableTypes( getTableTypeMeta( tableType ) ) ); @@ -269,7 +270,7 @@ private static TableType getTableTypeMeta( String tableType ) { } - static synchronized TypesResponse getTypes() { + public static synchronized TypesResponse getTypes() { TypesResponse.Builder responseBuilder = TypesResponse.newBuilder(); Arrays.stream( PolyType.values() ).forEach( polyType -> responseBuilder.addTypes( getTypeMeta( polyType ) ) ); return responseBuilder.build(); @@ -293,7 +294,7 @@ private static Type getTypeMeta( PolyType polyType ) { } - static ProceduresResponse getProcedures( String languageName, String procedureNamePattern ) { + public static ProceduresResponse getProcedures( String languageName, String procedureNamePattern ) { // TODO: get data after functionality is implemented return ProceduresResponse.newBuilder().build(); } @@ -318,7 +319,7 @@ private static ClientInfoPropertyMeta getClientInfoPropertyMeta( PIClientInfoPro } - static FunctionsResponse getFunctions( QueryLanguage language, FunctionCategory functionCategory ) { + public static FunctionsResponse getFunctions( QueryLanguage language, FunctionCategory functionCategory ) { List functions = OperatorRegistry.getMatchingOperators( language ).values().stream() .filter( o -> o instanceof org.polypheny.db.nodes.Function ) .map( org.polypheny.db.nodes.Function.class::cast ) @@ -339,7 +340,7 @@ private static Function getFunctionMeta( org.polypheny.db.nodes.Function functio } - static DbmsVersionResponse getDbmsVersion() { + public static DbmsVersionResponse getDbmsVersion() { try { String versionName = PolyphenyDb.class.getPackage().getImplementationVersion(); if ( versionName == null ) { diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/metaRetrieval/GraphMetaRetriever.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/metaRetrieval/GraphMetaRetriever.java new file mode 100644 index 0000000000..5efaeb05f1 --- /dev/null +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/metaRetrieval/GraphMetaRetriever.java @@ -0,0 +1,65 @@ +/* + * 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.prisminterface.metaRetrieval; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import org.polypheny.db.PolyImplementation; +import org.polypheny.db.algebra.type.AlgDataType; +import org.polypheny.db.algebra.type.AlgDataTypeField; +import org.polypheny.db.prisminterface.utils.PrismUtils; +import org.polypheny.db.processing.QueryProcessorHelpers; +import org.polypheny.db.type.PolyType; +import org.polypheny.prism.ColumnMeta; +import org.polypheny.prism.TypeMeta; + +public class GraphMetaRetriever { + + public static boolean retrievedResultIsRelational( PolyImplementation polyImplementation ) { + PolyType polyType = polyImplementation.tupleType.getFields().get( 0 ).getType().getPolyType(); + return switch ( polyType ) { + case NODE, EDGE, PATH -> false; + default -> true; + }; + } + + + public static List retrieveColumnMetas( PolyImplementation polyImplementation ) { + List columns = polyImplementation.tupleType.getFields(); + AtomicInteger columnIndexGenerator = new AtomicInteger(); + return columns.stream().map( c -> retieveColumnMeta( c, columnIndexGenerator ) ).toList(); + } + + + private static ColumnMeta retieveColumnMeta( AlgDataTypeField field, AtomicInteger columnIndexGenerator ) { + AlgDataType type = field.getType(); + TypeMeta typeMeta = TypeMeta.newBuilder() + .setProtoValueType( PrismUtils.getProtoFromPolyType( type.getPolyType() ) ) + .build(); + return ColumnMeta.newBuilder() + .setColumnIndex( columnIndexGenerator.getAndIncrement() ) + .setColumnLabel( field.getName() ) + .setColumnName( field.getName() ) + .setTypeMeta( typeMeta ) + .setIsNullable( type.isNullable() ) + .setLength( type.getPrecision() ) + .setPrecision( QueryProcessorHelpers.getPrecision( type ) ) // <- same as type.getPrecision() but returns 0 if not applicable + .setScale( type.getScale() ) + .build(); + } + +} diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/PIClientInfoProperties.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/metaRetrieval/PIClientInfoProperties.java similarity index 93% rename from plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/PIClientInfoProperties.java rename to plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/metaRetrieval/PIClientInfoProperties.java index caccd5b21a..a2a4206434 100644 --- a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/PIClientInfoProperties.java +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/metaRetrieval/PIClientInfoProperties.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package org.polypheny.db.prisminterface; +package org.polypheny.db.prisminterface.metaRetrieval; import java.util.Arrays; import java.util.List; import java.util.Properties; -class PIClientInfoProperties extends Properties { +public class PIClientInfoProperties extends Properties { private static final int MAX_STRING_LENGTH = 2147483647; static final List DEFAULTS = Arrays.asList( @@ -51,7 +51,7 @@ class PIClientInfoProperties extends Properties { ); - PIClientInfoProperties() { + public PIClientInfoProperties() { super(); DEFAULTS.forEach( p -> setProperty( p.key, p.default_value ) ); } @@ -62,3 +62,4 @@ record ClientInfoPropertiesDefault( String key, String default_value, int maxLen } } + diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/relational/RelationalMetaRetriever.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/metaRetrieval/RelationalMetaRetriever.java similarity index 96% rename from plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/relational/RelationalMetaRetriever.java rename to plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/metaRetrieval/RelationalMetaRetriever.java index f1ebf9c7bf..9a2cca94b6 100644 --- a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/relational/RelationalMetaRetriever.java +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/metaRetrieval/RelationalMetaRetriever.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.polypheny.db.prisminterface.relational; +package org.polypheny.db.prisminterface.metaRetrieval; import java.util.ArrayList; import java.util.List; @@ -27,6 +27,7 @@ import org.polypheny.db.algebra.type.AlgDataType; import org.polypheny.db.algebra.type.AlgDataTypeField; import org.polypheny.db.plan.AlgOptUtil; +import org.polypheny.db.prisminterface.utils.PrismUtils; import org.polypheny.db.processing.QueryProcessorHelpers; import org.polypheny.db.type.PolyType; import org.polypheny.prism.ArrayMeta; @@ -44,6 +45,21 @@ public class RelationalMetaRetriever { private static final int ORIGIN_SCHEMA_INDEX = 1; + public static List retrieveColumnMetas( PolyImplementation polyImplementation ) { + AlgDataType algDataType = retrieveAlgDataType( polyImplementation ); + AlgDataType whatever = QueryProcessorHelpers.makeStruct( polyImplementation.getStatement().getTransaction().getTypeFactory(), algDataType ); + List> origins = polyImplementation.getPreparedResult().getFieldOrigins(); + List columns = new ArrayList<>(); + int index = 0; + for ( Ord pair : Ord.zip( whatever.getFields() ) ) { + final AlgDataTypeField field = pair.e; + final AlgDataType type = field.getType(); + columns.add( retrieveColumnMeta( index++, field.getName(), type, origins.get( pair.i ) ) ); + } + return columns; + } + + public static List retrieveParameterMetas( AlgDataType parameterRowType ) { return parameterRowType.getFields().stream() .map( p -> retrieveParameterMeta( p, null ) ) @@ -63,26 +79,6 @@ private static ParameterMeta retrieveParameterMeta( AlgDataTypeField algDataType } - public static List retrieveColumnMetas( PolyImplementation polyImplementation ) { - AlgDataType algDataType = retrieveAlgDataType( polyImplementation ); - AlgDataType whatever = QueryProcessorHelpers.makeStruct( polyImplementation.getStatement().getTransaction().getTypeFactory(), algDataType ); - List> origins = polyImplementation.getPreparedResult().getFieldOrigins(); - List columns = new ArrayList<>(); - int index = 0; - for ( Ord pair : Ord.zip( whatever.getFields() ) ) { - final AlgDataTypeField field = pair.e; - final AlgDataType type = field.getType(); - columns.add( retrieveColumnMeta( index++, field.getName(), type, origins.get( pair.i ) ) ); - } - return columns; - } - - - private static ProtoPolyType getFromPolyType( PolyType polyType ) { - return ProtoPolyType.valueOf( polyType.getName() ); - } - - private static ColumnMeta retrieveColumnMeta( int index, String fieldName, @@ -156,7 +152,7 @@ private static TypeMeta retrieveTypeMeta( AlgDataType algDataType ) { //TODO TH: handle structured type meta in a useful way .build(); } - ProtoPolyType type = getFromPolyType( polyType ); + ProtoPolyType type = PrismUtils.getProtoFromPolyType( polyType ); return TypeMeta.newBuilder() .setProtoValueType( type ) .build(); diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/DocumentExecutor.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/DocumentExecutor.java index 8f962b6075..fc57daac72 100644 --- a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/DocumentExecutor.java +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/DocumentExecutor.java @@ -20,7 +20,9 @@ import org.apache.commons.lang3.time.StopWatch; import org.polypheny.db.PolyImplementation; import org.polypheny.db.ResultIterator; +import org.polypheny.db.algebra.constant.Kind; import org.polypheny.db.catalog.logistic.DataModel; +import org.polypheny.db.monitoring.events.MonitoringType; import org.polypheny.db.prisminterface.PIClient; import org.polypheny.db.prisminterface.PIServiceException; import org.polypheny.db.prisminterface.statements.PIStatement; @@ -43,62 +45,38 @@ DataModel getDataModel() { @Override StatementResult executeAndGetResult( PIStatement piStatement ) { - if ( hasInvalidNamespaceType( piStatement ) ) { - throw new PIServiceException( "The results of type " - + piStatement.getLanguage().dataModel() - + "returned by this statement can't be retrieved by a document retriever.", - "I9000", - 9000 - ); - } - PolyImplementation implementation = piStatement.getImplementation(); - if ( implementation == null ) { - throw new PIServiceException( "Can't retrieve results form an unexecuted statement.", - "I9002", - 9002 - ); - } - PIClient client = piStatement.getClient(); + throwOnIllegalState( piStatement ); StatementResult.Builder resultBuilder = StatementResult.newBuilder(); - if ( implementation.isDDL() ) { + if ( piStatement.getImplementation().isDDL() ) { resultBuilder.setScalar( 1 ); return resultBuilder.build(); } - throw new PIServiceException( "Can't execute a non DDL or non DML statement using this method..", - "I9003", - 9002 - ); + throw new PIServiceException( "Can't execute a non DDL or non DML statement using this method." ); } @Override StatementResult executeAndGetResult( PIStatement piStatement, int fetchSize ) { - if ( hasInvalidNamespaceType( piStatement ) ) { - throw new PIServiceException( "The results of type " - + piStatement.getLanguage().dataModel() - + "returned by this statement can't be retrieved by a document retriever.", - "I9000", - 9000 - ); - } + throwOnIllegalState( piStatement ); + Statement statement = piStatement.getStatement(); PolyImplementation implementation = piStatement.getImplementation(); - if ( implementation == null ) { - throw new PIServiceException( "Can't retrieve results form an unexecuted statement.", - "I9002", - 9002 - ); - } PIClient client = piStatement.getClient(); StatementResult.Builder resultBuilder = StatementResult.newBuilder(); - if ( implementation.isDDL() ) { + if ( Kind.DDL.contains( implementation.getKind() ) ) { resultBuilder.setScalar( 1 ); return resultBuilder.build(); } + if ( Kind.DML.contains( implementation.getKind() ) ) { + try ( ResultIterator iterator = implementation.execute( statement, -1 ) ) { + resultBuilder.setScalar( PolyImplementation.getRowsChanged( statement, iterator.getIterator(), MonitoringType.from( implementation.getKind() ) ) ); + } + client.commitCurrentTransactionIfAuto(); + return resultBuilder.build(); + } piStatement.setIterator( implementation.execute( piStatement.getStatement(), fetchSize ) ); Frame frame = fetch( piStatement, fetchSize ); resultBuilder.setFrame( frame ); if ( frame.getIsLast() ) { - //TODO TH: special handling for result set updates. Do we need to wait with committing until all changes have been done? client.commitCurrentTransactionIfAuto(); } return resultBuilder.build(); @@ -107,40 +85,17 @@ StatementResult executeAndGetResult( PIStatement piStatement, int fetchSize ) { @Override Frame fetch( PIStatement piStatement, int fetchSize ) { - if ( hasInvalidNamespaceType( piStatement ) ) { - throw new PIServiceException( "The results of type " - + piStatement.getLanguage().dataModel() - + "returned by this statement can't be retrieved by a document retriever.", - "I9000", - 9000 - ); - } + throwOnIllegalState( piStatement ); StopWatch executionStopWatch = piStatement.getExecutionStopWatch(); - Statement statement = piStatement.getStatement(); - if ( statement == null ) { - throw new PIServiceException( "Statement is not linked to a polypheny statement", - "I9001", - 9001 - ); - } - PolyImplementation implementation = piStatement.getImplementation(); - if ( implementation == null ) { - throw new PIServiceException( "Can't fetch from an unexecuted statement.", - "I9002", - 9002 - ); - } ResultIterator iterator = piStatement.getIterator(); - if ( iterator == null ) { - throw new PIServiceException( "Can't fetch from an unexecuted statement.", - "I9002", - 9002 - ); - } startOrResumeStopwatch( executionStopWatch ); List data = iterator.getNextBatch( fetchSize ).stream().map( p -> p.get( 0 ) ).toList(); - executionStopWatch.stop(); - return PrismUtils.buildDocumentFrame( !iterator.hasMoreRows(), data ); + boolean isLast = !iterator.hasMoreRows(); + if ( isLast ) { + executionStopWatch.stop(); + piStatement.getImplementation().getExecutionTimeMonitor().setExecutionTime( executionStopWatch.getNanoTime() ); + } + return PrismUtils.buildDocumentFrame( isLast, data ); } } diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/Executor.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/Executor.java index 2030a84be3..865f45d242 100644 --- a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/Executor.java +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/Executor.java @@ -17,7 +17,9 @@ package org.polypheny.db.prisminterface.statementProcessing; import org.apache.commons.lang3.time.StopWatch; +import org.polypheny.db.PolyImplementation; import org.polypheny.db.catalog.logistic.DataModel; +import org.polypheny.db.prisminterface.PIServiceException; import org.polypheny.db.prisminterface.statements.PIStatement; import org.polypheny.prism.Frame; import org.polypheny.prism.StatementResult; @@ -36,8 +38,26 @@ protected void startOrResumeStopwatch( StopWatch stopWatch ) { } - protected boolean hasInvalidNamespaceType( PIStatement piStatement ) { - return piStatement.getLanguage().dataModel() != getDataModel(); + protected void throwOnIllegalState( PIStatement piStatement ) throws PIServiceException { + DataModel statementDataModel = piStatement.getLanguage().dataModel(); + + if ( statementDataModel != getDataModel() ) { + String message = String.format( + "The results of type %s returned by this statement can't be retrieved by a %s retriever.", + statementDataModel.name().toLowerCase(), + getDataModel().name().toLowerCase() + ); + throw new PIServiceException( message ); + } + + if ( piStatement.getStatement() == null ) { + throw new PIServiceException( "Statement is not linked to a polypheny statement" ); + } + + PolyImplementation implementation = piStatement.getImplementation(); + if ( implementation == null ) { + throw new PIServiceException( "Can't retrieve results form an unexecuted statement." ); + } } diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/GraphExecutor.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/GraphExecutor.java new file mode 100644 index 0000000000..4ba984e875 --- /dev/null +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/GraphExecutor.java @@ -0,0 +1,108 @@ +/* + * 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.prisminterface.statementProcessing; + +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.time.StopWatch; +import org.polypheny.db.PolyImplementation; +import org.polypheny.db.ResultIterator; +import org.polypheny.db.algebra.constant.Kind; +import org.polypheny.db.catalog.logistic.DataModel; +import org.polypheny.db.monitoring.events.MonitoringType; +import org.polypheny.db.prisminterface.PIClient; +import org.polypheny.db.prisminterface.PIServiceException; +import org.polypheny.db.prisminterface.metaRetrieval.GraphMetaRetriever; +import org.polypheny.db.prisminterface.statements.PIStatement; +import org.polypheny.db.prisminterface.utils.PrismUtils; +import org.polypheny.db.transaction.Statement; +import org.polypheny.db.type.entity.PolyValue; +import org.polypheny.prism.ColumnMeta; +import org.polypheny.prism.Frame; +import org.polypheny.prism.StatementResult; + +public class GraphExecutor extends Executor { + + private static final DataModel namespaceType = DataModel.GRAPH; + + + @Override + DataModel getDataModel() { + return namespaceType; + } + + + @Override + StatementResult executeAndGetResult( PIStatement piStatement ) { + throwOnIllegalState( piStatement ); + StatementResult.Builder resultBuilder = StatementResult.newBuilder(); + if ( piStatement.getImplementation().isDDL() ) { + resultBuilder.setScalar( 1 ); + return resultBuilder.build(); + } + throw new PIServiceException( "Can't execute a non DDL or non DML statement using this method." ); + } + + + @Override + StatementResult executeAndGetResult( PIStatement piStatement, int fetchSize ) { + throwOnIllegalState( piStatement ); + Statement statement = piStatement.getStatement(); + PolyImplementation implementation = piStatement.getImplementation(); + PIClient client = piStatement.getClient(); + StatementResult.Builder resultBuilder = StatementResult.newBuilder(); + if ( Kind.DDL.contains( implementation.getKind() ) ) { + resultBuilder.setScalar( 1 ); + return resultBuilder.build(); + } + if ( Kind.DML.contains( implementation.getKind() ) ) { + try ( ResultIterator iterator = implementation.execute( statement, -1 ) ) { + resultBuilder.setScalar( PolyImplementation.getRowsChanged( statement, iterator.getIterator(), MonitoringType.from( implementation.getKind() ) ) ); + } + client.commitCurrentTransactionIfAuto(); + return resultBuilder.build(); + } + piStatement.setIterator( implementation.execute( piStatement.getStatement(), fetchSize ) ); + Frame frame = fetch( piStatement, fetchSize ); + resultBuilder.setFrame( frame ); + if ( frame.getIsLast() ) { + client.commitCurrentTransactionIfAuto(); + } + return resultBuilder.build(); + } + + + @Override + Frame fetch( PIStatement piStatement, int fetchSize ) { + throwOnIllegalState( piStatement ); + StopWatch executionStopWatch = piStatement.getExecutionStopWatch(); + ResultIterator iterator = piStatement.getIterator(); + startOrResumeStopwatch( executionStopWatch ); + List> data = new ArrayList<>( iterator.getNextBatch( fetchSize ) ); + boolean isLast = !iterator.hasMoreRows(); + if ( isLast ) { + executionStopWatch.stop(); + piStatement.getImplementation().getExecutionTimeMonitor().setExecutionTime( executionStopWatch.getNanoTime() ); + } + if ( GraphMetaRetriever.retrievedResultIsRelational( piStatement.getImplementation() ) ) { + List columnMetas = GraphMetaRetriever.retrieveColumnMetas( piStatement.getImplementation() ); + return PrismUtils.buildRelationalFrame( isLast, data, columnMetas ); + } + return PrismUtils.buildGraphFrame( isLast, data ); + } + +} diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/NamedValueProcessor.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/NamedValueProcessor.java similarity index 97% rename from plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/NamedValueProcessor.java rename to plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/NamedValueProcessor.java index 8efe9f5c15..8b2371651f 100644 --- a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/NamedValueProcessor.java +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/NamedValueProcessor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.polypheny.db.prisminterface; +package org.polypheny.db.prisminterface.statementProcessing; import java.util.ArrayList; import java.util.List; diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/RelationalExecutor.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/RelationalExecutor.java index f73afa9446..a0155f2727 100644 --- a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/RelationalExecutor.java +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/RelationalExecutor.java @@ -25,7 +25,7 @@ import org.polypheny.db.monitoring.events.MonitoringType; import org.polypheny.db.prisminterface.PIClient; import org.polypheny.db.prisminterface.PIServiceException; -import org.polypheny.db.prisminterface.relational.RelationalMetaRetriever; +import org.polypheny.db.prisminterface.metaRetrieval.RelationalMetaRetriever; import org.polypheny.db.prisminterface.statements.PIStatement; import org.polypheny.db.prisminterface.utils.PrismUtils; import org.polypheny.db.transaction.Statement; @@ -47,67 +47,25 @@ DataModel getDataModel() { @Override StatementResult executeAndGetResult( PIStatement piStatement ) { - if ( hasInvalidNamespaceType( piStatement ) ) { - throw new PIServiceException( "The results of type " - + piStatement.getLanguage().dataModel() - + "returned by this statement can't be retrieved by a relational retriever.", - "I9000", - 9000 - ); - } + throwOnIllegalState( piStatement ); Statement statement = piStatement.getStatement(); - if ( statement == null ) { - throw new PIServiceException( "Statement is not linked to a polypheny statement", - "I9001", - 9001 - ); - } PolyImplementation implementation = piStatement.getImplementation(); - if ( implementation == null ) { - throw new PIServiceException( "Can't retrieve results from an unexecuted statement.", - "I9002", - 9002 - ); - } - PIClient client = piStatement.getClient(); StatementResult.Builder resultBuilder = StatementResult.newBuilder(); if ( implementation.isDDL() || Kind.DML.contains( implementation.getKind() ) ) { try ( ResultIterator iterator = implementation.execute( statement, -1 ) ) { resultBuilder.setScalar( PolyImplementation.getRowsChanged( statement, iterator.getIterator(), MonitoringType.from( implementation.getKind() ) ) ); } - client.commitCurrentTransactionIfAuto(); + piStatement.getClient().commitCurrentTransactionIfAuto(); return resultBuilder.build(); } - throw new PIServiceException( "Can't execute a non DDL or non DML statement using this method..", - "I9003", - 9002 - ); + throw new PIServiceException( "Can't execute a non DDL or non DML statement using this method." ); } public StatementResult executeAndGetResult( PIStatement piStatement, int fetchSize ) { - if ( hasInvalidNamespaceType( piStatement ) ) { - throw new PIServiceException( "The results of type " - + piStatement.getLanguage().dataModel() - + "returned by this statement can't be retrieved by a relational retriever.", - "I9000", - 9000 - ); - } + throwOnIllegalState( piStatement ); Statement statement = piStatement.getStatement(); - if ( statement == null ) { - throw new PIServiceException( "Statement is not linked to a polypheny statement", - "I9001", - 9001 - ); - } PolyImplementation implementation = piStatement.getImplementation(); - if ( implementation == null ) { - throw new PIServiceException( "Can't retrieve results from an unprepared statement.", - "I9002", - 9002 - ); - } PIClient client = piStatement.getClient(); StatementResult.Builder resultBuilder = StatementResult.newBuilder(); if ( Kind.DDL.contains( implementation.getKind() ) ) { @@ -134,29 +92,10 @@ public StatementResult executeAndGetResult( PIStatement piStatement, int fetchSi @Override public Frame fetch( PIStatement piStatement, int fetchSize ) { - if ( hasInvalidNamespaceType( piStatement ) ) { - throw new PIServiceException( "The results of type " - + piStatement.getLanguage().dataModel() - + "returned by this statement can't be retrieved by a relational retriever.", - "I9000", - 9000 - ); - } + throwOnIllegalState( piStatement ); StopWatch executionStopWatch = piStatement.getExecutionStopWatch(); PolyImplementation implementation = piStatement.getImplementation(); - if ( implementation == null ) { - throw new PIServiceException( "Can't fetch from an unprepared statement.", - "I9002", - 9002 - ); - } ResultIterator iterator = piStatement.getIterator(); - if ( iterator == null ) { - throw new PIServiceException( "Can't fetch from an unexecuted statement.", - "I9002", - 9002 - ); - } startOrResumeStopwatch( executionStopWatch ); List> rows = iterator.getNextBatch( fetchSize ); executionStopWatch.suspend(); diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/StatementProcessor.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/StatementProcessor.java index 9af54b719a..e76ee954b3 100644 --- a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/StatementProcessor.java +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statementProcessing/StatementProcessor.java @@ -16,18 +16,15 @@ package org.polypheny.db.prisminterface.statementProcessing; -import com.google.common.collect.ImmutableMap; import java.util.List; -import java.util.Map; import org.polypheny.db.algebra.type.AlgDataType; import org.polypheny.db.algebra.type.AlgDataTypeField; import org.polypheny.db.catalog.exceptions.GenericRuntimeException; -import org.polypheny.db.catalog.logistic.DataModel; import org.polypheny.db.languages.LanguageManager; import org.polypheny.db.languages.QueryLanguage; import org.polypheny.db.nodes.Node; import org.polypheny.db.prisminterface.PIServiceException; -import org.polypheny.db.prisminterface.relational.RelationalMetaRetriever; +import org.polypheny.db.prisminterface.metaRetrieval.RelationalMetaRetriever; import org.polypheny.db.prisminterface.statements.PIPreparedStatement; import org.polypheny.db.prisminterface.statements.PIStatement; import org.polypheny.db.processing.ImplementationContext; @@ -44,26 +41,21 @@ public class StatementProcessor { private static final String ORIGIN = "prism-interface"; - private static final Map RESULT_RETRIEVERS = - ImmutableMap.builder() - .put( DataModel.RELATIONAL, new RelationalExecutor() ) - .put( DataModel.DOCUMENT, new DocumentExecutor() ) - .build(); + private static final RelationalExecutor relationalExecutor = new RelationalExecutor(); + private static final GraphExecutor graphExecutor = new GraphExecutor(); + private static final DocumentExecutor documentExecutor = new DocumentExecutor(); public static void implement( PIStatement piStatement ) { Statement statement = piStatement.getStatement(); if ( statement == null ) { - throw new PIServiceException( "Statement is not linked to a PolyphenyStatement", - "I9003", - 9003 - ); + throw new PIServiceException( "Statement is not linked to a PolyphenyStatement" ); } QueryContext context = QueryContext.builder() .query( piStatement.getQuery() ) .language( piStatement.getLanguage() ) .namespaceId( piStatement.getNamespace().id ) - .transactionManager( piStatement.getTransaction().getTransactionManager() ) + .transactions( List.of( piStatement.getTransaction() ) ) .origin( ORIGIN ) .build(); List implementations = LanguageManager.getINSTANCE().anyPrepareQuery( context, statement ); @@ -90,27 +82,12 @@ public static void implement( PIStatement piStatement ) { public static StatementResult executeAndGetResult( PIStatement piStatement ) { - Executor executor = RESULT_RETRIEVERS.get( piStatement.getLanguage().dataModel() ); - if ( executor == null ) { - throw new PIServiceException( "No result retriever registered for namespace type " - + piStatement.getLanguage().dataModel(), - "I9004", - 9004 - ); - } - return executor.executeAndGetResult( piStatement ); + return getExecutor( piStatement ).executeAndGetResult( piStatement ); } public static StatementResult executeAndGetResult( PIStatement piStatement, int fetchSize ) { - Executor executor = RESULT_RETRIEVERS.get( piStatement.getLanguage().dataModel() ); - if ( executor == null ) { - throw new PIServiceException( "No result retriever registered for namespace type " - + piStatement.getLanguage().dataModel(), - "I9004", - 9004 - ); - } + Executor executor = getExecutor( piStatement ); try { return executor.executeAndGetResult( piStatement, fetchSize ); } catch ( Exception e ) { @@ -120,14 +97,7 @@ public static StatementResult executeAndGetResult( PIStatement piStatement, int public static Frame fetch( PIStatement piStatement, int fetchSize ) { - Executor executor = RESULT_RETRIEVERS.get( piStatement.getLanguage().dataModel() ); - if ( executor == null ) { - throw new PIServiceException( "No result retriever registered for namespace type " - + piStatement.getLanguage().dataModel(), - "I9004", - 9004 - ); - } + Executor executor = getExecutor( piStatement ); return executor.fetch( piStatement, fetchSize ); } @@ -147,4 +117,13 @@ public static void prepare( PIPreparedStatement piStatement ) { piStatement.setParameterPolyTypes( parameterRowType.getFields().stream().map( AlgDataTypeField::getType ).toList() ); } + + private static Executor getExecutor( PIStatement piStatement ) { + return switch ( piStatement.getLanguage().dataModel() ) { + case RELATIONAL -> relationalExecutor; + case GRAPH -> graphExecutor; + case DOCUMENT -> documentExecutor; + }; + } + } diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statements/PIPreparedNamedStatement.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statements/PIPreparedNamedStatement.java index 9dadddf7c0..7ca0559d1b 100644 --- a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statements/PIPreparedNamedStatement.java +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/statements/PIPreparedNamedStatement.java @@ -23,8 +23,8 @@ import org.polypheny.db.PolyImplementation; import org.polypheny.db.catalog.entity.logical.LogicalNamespace; import org.polypheny.db.languages.QueryLanguage; -import org.polypheny.db.prisminterface.NamedValueProcessor; import org.polypheny.db.prisminterface.PIClient; +import org.polypheny.db.prisminterface.statementProcessing.NamedValueProcessor; import org.polypheny.db.prisminterface.statementProcessing.StatementProcessor; import org.polypheny.db.transaction.Statement; import org.polypheny.db.transaction.Transaction; diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/utils/PolyValueSerializer.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/utils/PolyValueSerializer.java index f827b9d512..c0c78120b7 100644 --- a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/utils/PolyValueSerializer.java +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/utils/PolyValueSerializer.java @@ -17,6 +17,7 @@ package org.polypheny.db.prisminterface.utils; import com.google.protobuf.ByteString; +import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang3.NotImplementedException; @@ -28,6 +29,9 @@ import org.polypheny.db.type.entity.PolyValue; import org.polypheny.db.type.entity.category.PolyBlob; import org.polypheny.db.type.entity.document.PolyDocument; +import org.polypheny.db.type.entity.graph.PolyEdge; +import org.polypheny.db.type.entity.graph.PolyEdge.EdgeDirection; +import org.polypheny.db.type.entity.graph.PolyNode; import org.polypheny.db.type.entity.numerical.PolyBigDecimal; import org.polypheny.db.type.entity.numerical.PolyDouble; import org.polypheny.db.type.entity.numerical.PolyFloat; @@ -38,18 +42,21 @@ import org.polypheny.db.type.entity.temporal.PolyDate; import org.polypheny.db.type.entity.temporal.PolyTime; import org.polypheny.db.type.entity.temporal.PolyTimestamp; +import org.polypheny.prism.GraphElement; import org.polypheny.prism.ProtoBigDecimal; import org.polypheny.prism.ProtoBinary; import org.polypheny.prism.ProtoBoolean; import org.polypheny.prism.ProtoDate; import org.polypheny.prism.ProtoDocument; import org.polypheny.prism.ProtoDouble; -import org.polypheny.prism.ProtoEntry; +import org.polypheny.prism.ProtoEdge; +import org.polypheny.prism.ProtoEdge.Direction; import org.polypheny.prism.ProtoFloat; import org.polypheny.prism.ProtoInteger; import org.polypheny.prism.ProtoInterval; import org.polypheny.prism.ProtoList; import org.polypheny.prism.ProtoLong; +import org.polypheny.prism.ProtoNode; import org.polypheny.prism.ProtoNull; import org.polypheny.prism.ProtoString; import org.polypheny.prism.ProtoTime; @@ -64,16 +71,12 @@ public static List serializeList( List valuesList ) { } - public static List serializeToProtoEntryList( PolyMap polyMap ) { - return polyMap.entrySet().stream().map( PolyValueSerializer::serializeToProtoEntry ).toList(); - } + public static Map serializeToProtoMap( PolyMap polyMap ) { + Map map = new HashMap<>(); + polyMap.forEach( ( k, v ) -> map.put( k.asString().value, PolyValueSerializer.serialize( v ) ) ); - public static ProtoEntry serializeToProtoEntry( Map.Entry polyMapEntry ) { - return ProtoEntry.newBuilder() - .setKey( serialize( polyMapEntry.getKey() ) ) - .setValue( serialize( polyMapEntry.getValue() ) ) - .build(); + return map; } @@ -111,13 +114,6 @@ private static ProtoValue serializeGeometry( PolyGeometry geometry ) { } - public static ProtoDocument buildProtoDocument( PolyDocument polyDocument ) { - return ProtoDocument.newBuilder() - .addAllEntries( serializeToProtoEntryList( polyDocument.asMap() ) ) - .build(); - } - - private static ProtoValue serializeAsProtoDocument( PolyDocument polyDocument ) { return ProtoValue.newBuilder() .setDocument( buildProtoDocument( polyDocument ) ) @@ -287,7 +283,7 @@ public static ProtoValue serializeAsProtoTimestamp( PolyTimestamp polyTimestamp } - private static ProtoValue serializeAsProtoNull() { + public static ProtoValue serializeAsProtoNull() { return ProtoValue.newBuilder() .setNull( ProtoNull.newBuilder().build() ) .build(); @@ -307,4 +303,56 @@ public static ProtoValue serializeAsProtoBigDecimal( PolyBigDecimal polyBigDecim .build(); } + + public static ProtoDocument buildProtoDocument( PolyDocument polyDocument ) { + return ProtoDocument.newBuilder() + .putAllEntries( serializeToProtoMap( polyDocument.asMap() ) ) + .build(); + } + + + public static GraphElement buildProtoGraphElement( PolyValue value ) { + return switch ( value.getType() ) { + case NODE -> GraphElement.newBuilder().setNode( buildProtoNode( value.asNode() ) ).build(); + case EDGE -> GraphElement.newBuilder().setEdge( buildProtoEdge( value.asEdge() ) ).build(); + default -> throw new RuntimeException( "Invalid graph element " + value.getType() ); + }; + } + + + public static ProtoNode buildProtoNode( PolyNode polyNode ) { + ProtoNode.Builder node = ProtoNode.newBuilder() + .setId( polyNode.getId().getValue() ) + .addAllLabels( polyNode.getLabels().stream().map( PolyString::getValue ).toList() ) + .putAllProperties( serializeToProtoMap( polyNode.properties.asMap() ) ); + if ( polyNode.variableName != null ) { + node.setName( polyNode.variableName.getValue() ); + } + return node.build(); + } + + + public static ProtoEdge buildProtoEdge( PolyEdge polyEdge ) { + ProtoEdge.Builder edge = ProtoEdge.newBuilder() + .setId( polyEdge.getId().getValue() ) + .addAllLabels( polyEdge.getLabels().stream().map( PolyString::getValue ).toList() ) + .putAllProperties( serializeToProtoMap( polyEdge.properties.asMap() ) ) + .setLeft( polyEdge.getLeft().getValue() ) + .setRight( polyEdge.getRight().getValue() ) + .setDirection( buildProtoEdgeDirection( polyEdge.getDirection() ) ); + if ( polyEdge.variableName != null ) { + edge.setName( polyEdge.getVariableName().getValue() ); + } + return edge.build(); + } + + + private static Direction buildProtoEdgeDirection( EdgeDirection direction ) { + return switch ( direction ) { + case LEFT_TO_RIGHT -> Direction.LEFT_TO_RIGHT; + case RIGHT_TO_LEFT -> Direction.RIGHT_TO_LEFT; + case NONE -> Direction.NONE; + }; + } + } diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/utils/PrismUtils.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/utils/PrismUtils.java index 0b27f6f57c..ee0de6e602 100644 --- a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/utils/PrismUtils.java +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/utils/PrismUtils.java @@ -17,15 +17,18 @@ package org.polypheny.db.prisminterface.utils; import java.util.List; -import org.polypheny.db.catalog.exceptions.GenericRuntimeException; import org.polypheny.db.prisminterface.statements.PIPreparedStatement; import org.polypheny.db.prisminterface.statements.PIStatement; +import org.polypheny.db.type.PolyType; import org.polypheny.db.type.entity.PolyValue; import org.polypheny.prism.ColumnMeta; import org.polypheny.prism.DocumentFrame; import org.polypheny.prism.Frame; +import org.polypheny.prism.GraphElement; +import org.polypheny.prism.GraphFrame; import org.polypheny.prism.PreparedStatementSignature; import org.polypheny.prism.ProtoDocument; +import org.polypheny.prism.ProtoPolyType; import org.polypheny.prism.RelationalFrame; import org.polypheny.prism.Row; import org.polypheny.prism.StatementBatchResponse; @@ -73,7 +76,7 @@ public static PreparedStatementSignature createPreparedStatementSignature( PIPre } - public static Row serializeToRow( List row ) { + private static Row serializeToRow( List row ) { return Row.newBuilder() .addAllValues( PolyValueSerializer.serializeList( row ) ) .build(); @@ -85,6 +88,11 @@ public static List serializeToRows( List> rows ) { } + private static List serializeToGraphElement( List> data ) { + return data.stream().map( e -> PolyValueSerializer.buildProtoGraphElement( e.get( 0 ) ) ).toList(); + } + + public static Frame buildRelationalFrame( boolean isLast, List> rows, List metas ) { RelationalFrame relationalFrame = RelationalFrame.newBuilder() .addAllColumnMeta( metas ) @@ -112,8 +120,21 @@ public static Frame buildDocumentFrame( boolean isLast, List data ) { } - public static Frame buildGraphFrame() { - throw new GenericRuntimeException( "Feature not implemented" ); + public static Frame buildGraphFrame( boolean isLast, List> data ) { + GraphFrame.Builder graphFrameBuilder = GraphFrame.newBuilder(); + if ( !data.isEmpty() ) { + graphFrameBuilder.addAllElement( serializeToGraphElement( data ) ); + } + + return Frame.newBuilder() + .setIsLast( isLast ) + .setGraphFrame( graphFrameBuilder.build() ) + .build(); + } + + + public static ProtoPolyType getProtoFromPolyType( PolyType polyType ) { + return ProtoPolyType.valueOf( polyType.getName() ); } } diff --git a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/utils/PrismValueDeserializer.java b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/utils/PrismValueDeserializer.java index 5226f99a6a..76e6d01806 100644 --- a/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/utils/PrismValueDeserializer.java +++ b/plugins/prism-interface/src/main/java/org/polypheny/db/prisminterface/utils/PrismValueDeserializer.java @@ -42,7 +42,6 @@ import org.polypheny.prism.IndexedParameters; import org.polypheny.prism.ProtoBigDecimal; import org.polypheny.prism.ProtoValue; -import org.polypheny.prism.ProtoValue.ValueCase; public class PrismValueDeserializer { @@ -104,11 +103,10 @@ public static PolyValue deserializeProtoValue( ProtoValue protoValue ) { private static PolyValue deserializeToPolyDocument( ProtoValue protoValue ) { PolyDocument document = new PolyDocument(); - protoValue.getDocument().getEntriesList().stream() - .filter( e -> e.getKey().getValueCase() == ValueCase.STRING ) - .forEach( e -> document.put( - new PolyString( e.getKey().getString().getString() ), - deserializeProtoValue( e.getValue() ) + protoValue.getDocument().getEntriesMap() + .forEach( ( k, v ) -> document.put( + new PolyString( k ), + deserializeProtoValue( v ) ) ); return document; } diff --git a/plugins/prism-interface/src/test/java/org/polypheny/db/prisminterface/NamedValueProcessorTest.java b/plugins/prism-interface/src/test/java/org/polypheny/db/prisminterface/NamedValueProcessorTest.java index 57a49f0ff9..00f7592287 100644 --- a/plugins/prism-interface/src/test/java/org/polypheny/db/prisminterface/NamedValueProcessorTest.java +++ b/plugins/prism-interface/src/test/java/org/polypheny/db/prisminterface/NamedValueProcessorTest.java @@ -26,6 +26,7 @@ import org.junit.jupiter.api.Test; import org.polypheny.db.TestHelper; import org.polypheny.db.catalog.exceptions.GenericRuntimeException; +import org.polypheny.db.prisminterface.statementProcessing.NamedValueProcessor; import org.polypheny.db.type.entity.PolyString; import org.polypheny.db.type.entity.PolyValue; diff --git a/plugins/prism-interface/src/test/java/org/polypheny/db/prisminterface/PrismUtilsTest.java b/plugins/prism-interface/src/test/java/org/polypheny/db/prisminterface/PrismUtilsTest.java new file mode 100644 index 0000000000..9b1d4eaf95 --- /dev/null +++ b/plugins/prism-interface/src/test/java/org/polypheny/db/prisminterface/PrismUtilsTest.java @@ -0,0 +1,141 @@ +/* + * 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.prisminterface; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.polypheny.db.TestHelper; +import org.polypheny.db.prisminterface.utils.PrismUtils; +import org.polypheny.db.type.entity.PolyBoolean; +import org.polypheny.db.type.entity.PolyString; +import org.polypheny.db.type.entity.PolyValue; +import org.polypheny.db.type.entity.graph.PolyDictionary; +import org.polypheny.db.type.entity.graph.PolyEdge; +import org.polypheny.db.type.entity.graph.PolyEdge.EdgeDirection; +import org.polypheny.db.type.entity.graph.PolyNode; +import org.polypheny.db.type.entity.numerical.PolyInteger; +import org.polypheny.prism.Frame; +import org.polypheny.prism.ProtoValue; +import org.polypheny.prism.ProtoValue.ValueCase; +import org.polypheny.prism.Row; + +public class PrismUtilsTest { + + @BeforeAll + public static void init() { + // needed to launch polypheny + TestHelper.getInstance(); + } + + + @Test + public void serializeToRowsSimpleTest() { + List row1 = List.of( new PolyString( "test" ), new PolyInteger( 42 ), new PolyBoolean( false ) ); + List row2 = List.of( new PolyString( "example" ), new PolyInteger( 24 ), new PolyBoolean( true ) ); + + List> inputRows = List.of( row1, row2 ); + List prismRows = PrismUtils.serializeToRows( inputRows ); + + Row prismRow1 = prismRows.get( 0 ); + List outputRow1 = prismRow1.getValuesList(); + assertEquals( ValueCase.STRING, outputRow1.get( 0 ).getValueCase() ); + assertEquals( ValueCase.INTEGER, outputRow1.get( 1 ).getValueCase() ); + assertEquals( ValueCase.BOOLEAN, outputRow1.get( 2 ).getValueCase() ); + assertEquals( "test", outputRow1.get( 0 ).getString().getString() ); + assertEquals( 42, outputRow1.get( 1 ).getInteger().getInteger() ); + assertFalse( outputRow1.get( 2 ).getBoolean().getBoolean() ); + + Row prismRow2 = prismRows.get( 1 ); + List outputRow2 = prismRow2.getValuesList(); + assertEquals( ValueCase.STRING, outputRow2.get( 0 ).getValueCase() ); + assertEquals( ValueCase.INTEGER, outputRow2.get( 1 ).getValueCase() ); + assertEquals( ValueCase.BOOLEAN, outputRow2.get( 2 ).getValueCase() ); + assertEquals( "example", outputRow2.get( 0 ).getString().getString() ); + assertEquals( 24, outputRow2.get( 1 ).getInteger().getInteger() ); + assertTrue( outputRow2.get( 2 ).getBoolean().getBoolean() ); + } + + + @Test + public void serializeToRowsEmptyRowTest() { + List> inputRows = List.of(); + List prismRows = PrismUtils.serializeToRows( inputRows ); + assertTrue( prismRows.isEmpty() ); + } + + + @Test + public void buildGraphFrameWithNodesTest() { + PolyDictionary properties1 = PolyDictionary.ofDict( Map.of( new PolyString( "key1" ), new PolyString( "value1" ) ) ); + PolyDictionary properties2 = PolyDictionary.ofDict( Map.of( new PolyString( "key2" ), new PolyString( "value2" ) ) ); + + PolyNode node1 = new PolyNode( properties1, List.of( new PolyString( "label1" ) ), new PolyString( "node1" ) ); + PolyNode node2 = new PolyNode( properties2, List.of( new PolyString( "label2" ) ), new PolyString( "node2" ) ); + + List> data = List.of( + List.of( node1 ), + List.of( node2 ) + ); + + Frame result = PrismUtils.buildGraphFrame( true, data ); + + assertTrue( result.getIsLast() ); + assertEquals( 2, result.getGraphFrame().getElementCount() ); + assertEquals( "node1", result.getGraphFrame().getElement( 0 ).getNode().getName() ); + assertEquals( "node2", result.getGraphFrame().getElement( 1 ).getNode().getName() ); + } + + + @Test + public void buildGraphFrameWithEmptyNodesTest() { + List> data = List.of(); + + Frame result = PrismUtils.buildGraphFrame( true, data ); + + assertTrue( result.getIsLast() ); + assertEquals( 0, result.getGraphFrame().getElementCount() ); + } + + + @Test + public void buildGraphFrameWithEdgesTest() { + PolyDictionary properties1 = PolyDictionary.ofDict( Map.of( new PolyString( "key1" ), new PolyString( "value1" ) ) ); + PolyDictionary properties2 = PolyDictionary.ofDict( Map.of( new PolyString( "key2" ), new PolyString( "value2" ) ) ); + + PolyEdge node1 = new PolyEdge( properties1, List.of( new PolyString( "label1" ) ), new PolyString( "node4" ), new PolyString( "node5" ), EdgeDirection.NONE, new PolyString( "edge1" ) ); + PolyEdge node2 = new PolyEdge( properties2, List.of( new PolyString( "label2" ) ), new PolyString( "node4" ), new PolyString( "node5" ), EdgeDirection.NONE, new PolyString( "edge2" ) ); + + List> data = List.of( + List.of( node1 ), + List.of( node2 ) + ); + + Frame result = PrismUtils.buildGraphFrame( false, data ); + + assertFalse( result.getIsLast() ); + assertEquals( 2, result.getGraphFrame().getElementCount() ); + assertEquals( "edge1", result.getGraphFrame().getElement( 0 ).getEdge().getName() ); + assertEquals( "edge2", result.getGraphFrame().getElement( 1 ).getEdge().getName() ); + } + +}