diff --git a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelViewScan.java b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelViewScan.java index ad135122e1..b2f15c3dbb 100644 --- a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelViewScan.java +++ b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelViewScan.java @@ -26,6 +26,7 @@ import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.algebra.core.relational.RelScan; import org.polypheny.db.algebra.type.AlgDataType; +import org.polypheny.db.catalog.Catalog; import org.polypheny.db.catalog.entity.Entity; import org.polypheny.db.catalog.entity.logical.LogicalView; import org.polypheny.db.plan.AlgCluster; @@ -63,7 +64,7 @@ public static AlgNode create( AlgCluster cluster, final Entity entity ) { } ); LogicalView logicalView = entity.unwrap( LogicalView.class ).orElseThrow(); - AlgCollation algCollation = logicalView.getAlgCollation(); + AlgCollation algCollation = Catalog.snapshot().rel().getCollationInfo( entity.id ); return new LogicalRelViewScan( cluster, traitSet, entity, logicalView.prepareView( cluster ), algCollation ); } diff --git a/core/src/main/java/org/polypheny/db/catalog/catalogs/LogicalRelationalCatalog.java b/core/src/main/java/org/polypheny/db/catalog/catalogs/LogicalRelationalCatalog.java index d9e35236a6..b1350707df 100644 --- a/core/src/main/java/org/polypheny/db/catalog/catalogs/LogicalRelationalCatalog.java +++ b/core/src/main/java/org/polypheny/db/catalog/catalogs/LogicalRelationalCatalog.java @@ -326,8 +326,12 @@ public interface LogicalRelationalCatalog extends LogicalCatalog { Map getConstraints(); + void setNodeAndCollation( long id, AlgNode node, AlgCollation collation ); + Map getNodes(); + Map getCollations(); + void deleteKey( long id ); } diff --git a/core/src/main/java/org/polypheny/db/catalog/entity/logical/LogicalMaterializedView.java b/core/src/main/java/org/polypheny/db/catalog/entity/logical/LogicalMaterializedView.java index 58748a2989..59a55e6419 100644 --- a/core/src/main/java/org/polypheny/db/catalog/entity/logical/LogicalMaterializedView.java +++ b/core/src/main/java/org/polypheny/db/catalog/entity/logical/LogicalMaterializedView.java @@ -26,7 +26,6 @@ import lombok.EqualsAndHashCode; import lombok.Value; import lombok.experimental.SuperBuilder; -import org.polypheny.db.algebra.AlgCollation; import org.polypheny.db.catalog.entity.MaterializedCriteria; import org.polypheny.db.catalog.logistic.EntityType; import org.polypheny.db.languages.QueryLanguage; @@ -44,14 +43,27 @@ public class LogicalMaterializedView extends LogicalView { public boolean ordered; + public LogicalMaterializedView( + long id, + String name, + long namespaceId, + String query, + Map> underlyingTables, + QueryLanguage language, + MaterializedCriteria materializedCriteria, + boolean ordered + ) { + this( id, name, namespaceId, query, underlyingTables, language.serializedName(), materializedCriteria, ordered ); + } + + public LogicalMaterializedView( @Deserialize("id") long id, @Deserialize("name") String name, @Deserialize("namespaceId") long namespaceId, - @Deserialize("entityType") String query, - @Deserialize("algCollation") AlgCollation algCollation, + @Deserialize("query") String query, @Deserialize("underlyingTables") Map> underlyingTables, - @Deserialize("language") QueryLanguage language, + @Deserialize("languageName") String languageName, @Deserialize("materializedCriteria") MaterializedCriteria materializedCriteria, @Deserialize("ordered") boolean ordered ) { @@ -61,9 +73,8 @@ public LogicalMaterializedView( namespaceId, EntityType.MATERIALIZED_VIEW, query, - algCollation, underlyingTables, - language ); + languageName ); Map> map = new HashMap<>(); for ( Entry> e : underlyingTables.entrySet() ) { diff --git a/core/src/main/java/org/polypheny/db/catalog/entity/logical/LogicalView.java b/core/src/main/java/org/polypheny/db/catalog/entity/logical/LogicalView.java index ddd03dd91b..a156720d51 100644 --- a/core/src/main/java/org/polypheny/db/catalog/entity/logical/LogicalView.java +++ b/core/src/main/java/org/polypheny/db/catalog/entity/logical/LogicalView.java @@ -28,7 +28,6 @@ import lombok.experimental.NonFinal; import lombok.experimental.SuperBuilder; import org.polypheny.db.algebra.AbstractAlgNode; -import org.polypheny.db.algebra.AlgCollation; import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.algebra.BiAlg; import org.polypheny.db.algebra.SingleAlg; @@ -51,22 +50,32 @@ public class LogicalView extends LogicalTable { @Serialize public ImmutableMap> underlyingTables; @Serialize + public String languageName; public QueryLanguage language; @Serialize - public AlgCollation algCollation; - @Serialize public String query; + public LogicalView( + long id, + String name, + long namespaceId, + EntityType entityType, + String query, + Map> underlyingTables, + QueryLanguage language ) { + this( id, name, namespaceId, entityType, query, underlyingTables, language.serializedName() ); + } + + public LogicalView( @Deserialize("id") long id, @Deserialize("name") String name, @Deserialize("namespaceId") long namespaceId, @Deserialize("entityType") EntityType entityType, @Deserialize("query") String query, - @Deserialize("algCollation") AlgCollation algCollation, @Deserialize("underlyingTables") Map> underlyingTables, - @Deserialize("language") QueryLanguage language ) { + @Deserialize("languageName") String languageName ) { super( id, name, @@ -75,9 +84,9 @@ public LogicalView( null, false ); this.query = query; - this.algCollation = algCollation; this.underlyingTables = ImmutableMap.copyOf( underlyingTables ); - this.language = language; + this.languageName = languageName; + this.language = QueryLanguage.from( languageName ); } diff --git a/core/src/main/java/org/polypheny/db/catalog/impl/PolyCatalog.java b/core/src/main/java/org/polypheny/db/catalog/impl/PolyCatalog.java index d3490b7bbc..4a97ed4515 100644 --- a/core/src/main/java/org/polypheny/db/catalog/impl/PolyCatalog.java +++ b/core/src/main/java/org/polypheny/db/catalog/impl/PolyCatalog.java @@ -46,6 +46,7 @@ import org.polypheny.db.adapter.AdapterManager.Function5; import org.polypheny.db.adapter.DeployMode; import org.polypheny.db.adapter.java.AdapterTemplate; +import org.polypheny.db.algebra.AlgRoot; import org.polypheny.db.catalog.Catalog; import org.polypheny.db.catalog.IdBuilder; import org.polypheny.db.catalog.catalogs.AdapterCatalog; @@ -62,6 +63,7 @@ import org.polypheny.db.catalog.entity.LogicalQueryInterface; import org.polypheny.db.catalog.entity.LogicalUser; import org.polypheny.db.catalog.entity.logical.LogicalNamespace; +import org.polypheny.db.catalog.entity.logical.LogicalView; import org.polypheny.db.catalog.entity.physical.PhysicalEntity; import org.polypheny.db.catalog.exceptions.GenericRuntimeException; import org.polypheny.db.catalog.impl.allocation.PolyAllocDocCatalog; @@ -71,13 +73,19 @@ import org.polypheny.db.catalog.impl.logical.GraphCatalog; import org.polypheny.db.catalog.impl.logical.RelationalCatalog; import org.polypheny.db.catalog.logistic.DataModel; +import org.polypheny.db.catalog.logistic.Pattern; import org.polypheny.db.catalog.persistance.FilePersister; import org.polypheny.db.catalog.persistance.InMemoryPersister; import org.polypheny.db.catalog.persistance.Persister; import org.polypheny.db.catalog.snapshot.Snapshot; import org.polypheny.db.catalog.snapshot.impl.SnapshotBuilder; import org.polypheny.db.catalog.util.ConstraintCondition; +import org.polypheny.db.config.RuntimeConfig; import org.polypheny.db.iface.QueryInterfaceManager.QueryInterfaceTemplate; +import org.polypheny.db.nodes.Node; +import org.polypheny.db.processing.Processor; +import org.polypheny.db.processing.QueryContext.ParsedQueryContext; +import org.polypheny.db.transaction.Statement; import org.polypheny.db.transaction.Transaction; import org.polypheny.db.type.PolySerializable; import org.polypheny.db.util.Pair; @@ -534,6 +542,31 @@ public void restore( Transaction transaction ) { } ); updateSnapshot(); + + restoreViews( transaction ); + + updateSnapshot(); + } + + + private void restoreViews( Transaction transaction ) { + Statement statement = transaction.createStatement(); + snapshot.rel().getTables( (Pattern) null, null ).forEach( table -> { + if ( table instanceof LogicalView view ) { + Processor sqlProcessor = statement.getTransaction().getProcessor( view.language ); + Node node = sqlProcessor.parse( view.query ).get( 0 ); + AlgRoot algRoot = sqlProcessor.translate( statement, + ParsedQueryContext.builder() + .query( view.query ) + .language( view.language ) + .queryNode( sqlProcessor.validate( + statement.getTransaction(), node, RuntimeConfig.ADD_DEFAULT_VALUES_IN_INSERTS.getBoolean() ).left ) + .origin( statement.getTransaction().getOrigin() ) + .build() ); + getLogicalRel( view.namespaceId ).setNodeAndCollation( view.id, algRoot.alg, algRoot.collation ); + } + } ); + transaction.commit(); } diff --git a/core/src/main/java/org/polypheny/db/catalog/impl/logical/RelationalCatalog.java b/core/src/main/java/org/polypheny/db/catalog/impl/logical/RelationalCatalog.java index 568eedd67e..2f18933e64 100644 --- a/core/src/main/java/org/polypheny/db/catalog/impl/logical/RelationalCatalog.java +++ b/core/src/main/java/org/polypheny/db/catalog/impl/logical/RelationalCatalog.java @@ -20,6 +20,7 @@ import io.activej.serializer.BinarySerializer; import io.activej.serializer.annotations.Deserialize; import io.activej.serializer.annotations.Serialize; +import io.activej.serializer.annotations.SerializeClass; import java.beans.PropertyChangeSupport; import java.sql.Timestamp; import java.util.HashSet; @@ -80,13 +81,14 @@ public class RelationalCatalog implements PolySerializable, LogicalRelationalCat @Serialize @JsonProperty - public Map tables; + public Map tables; @Serialize @JsonProperty public Map columns; public Map nodes; + public Map collations; @Serialize @@ -127,6 +129,7 @@ public RelationalCatalog( this.keys = new ConcurrentHashMap<>( keys ); this.constraints = new ConcurrentHashMap<>( constraints ); this.nodes = new ConcurrentHashMap<>(); + this.collations = new ConcurrentHashMap<>(); listeners.addPropertyChangeListener( Catalog.getInstance().getChangeListener() ); } @@ -162,10 +165,11 @@ public LogicalTable addTable( String name, EntityType entityType, boolean modifi public LogicalView addView( String name, long namespaceId, boolean modifiable, AlgNode definition, AlgCollation algCollation, Map> underlyingTables, List connectedViews, AlgDataType fieldList, String query, QueryLanguage language ) { long id = idBuilder.getNewLogicalId(); - LogicalView view = new LogicalView( id, name, namespaceId, EntityType.VIEW, query, algCollation, underlyingTables, language ); + LogicalView view = new LogicalView( id, name, namespaceId, EntityType.VIEW, query, underlyingTables, language ); tables.put( id, view ); nodes.put( id, definition ); + collations.put( id, algCollation ); change( CatalogEvent.VIEW_CREATED, null, id ); return view; } @@ -180,7 +184,6 @@ public LogicalMaterializedView addMaterializedView( final String name, long name name, namespaceId, query, - algCollation, underlyingTables, language, materializedCriteria, @@ -189,6 +192,7 @@ public LogicalMaterializedView addMaterializedView( final String name, long name tables.put( id, materializedViewTable ); nodes.put( id, definition ); + collations.put( id, algCollation ); change( CatalogEvent.MATERIALIZED_VIEW_CREATED, null, id ); return materializedViewTable; } @@ -589,4 +593,10 @@ public boolean isTableFlaggedForDeletion( long tableId ) { return tablesFlaggedForDeletion.contains( tableId ); } + + public void setNodeAndCollation( long id, AlgNode node, AlgCollation collation ) { + this.nodes.put( id, node ); + this.collations.put( id, collation ); + } + } diff --git a/core/src/main/java/org/polypheny/db/catalog/snapshot/LogicalRelSnapshot.java b/core/src/main/java/org/polypheny/db/catalog/snapshot/LogicalRelSnapshot.java index 905b0b69a3..a4b489e797 100644 --- a/core/src/main/java/org/polypheny/db/catalog/snapshot/LogicalRelSnapshot.java +++ b/core/src/main/java/org/polypheny/db/catalog/snapshot/LogicalRelSnapshot.java @@ -21,6 +21,7 @@ import javax.annotation.Nullable; import lombok.NonNull; import org.jetbrains.annotations.NotNull; +import org.polypheny.db.algebra.AlgCollation; import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.catalog.Catalog; import org.polypheny.db.catalog.entity.LogicalConstraint; @@ -288,6 +289,7 @@ public interface LogicalRelSnapshot { AlgNode getNodeInfo( long id ); + AlgCollation getCollationInfo( long id ); List getConnectedViews( long id ); diff --git a/core/src/main/java/org/polypheny/db/catalog/snapshot/impl/LogicalRelSnapshotImpl.java b/core/src/main/java/org/polypheny/db/catalog/snapshot/impl/LogicalRelSnapshotImpl.java index 758ca873e4..4e93cc1046 100644 --- a/core/src/main/java/org/polypheny/db/catalog/snapshot/impl/LogicalRelSnapshotImpl.java +++ b/core/src/main/java/org/polypheny/db/catalog/snapshot/impl/LogicalRelSnapshotImpl.java @@ -33,6 +33,7 @@ import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.polypheny.db.algebra.AlgCollation; import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.catalog.catalogs.LogicalRelationalCatalog; import org.polypheny.db.catalog.entity.LogicalConstraint; @@ -101,6 +102,8 @@ public class LogicalRelSnapshotImpl implements LogicalRelSnapshot { ImmutableMap nodes; + ImmutableMap collations; + ImmutableMap> connectedViews; @@ -152,6 +155,8 @@ public LogicalRelSnapshotImpl( Map catalogs ) { /// ALGNODES e.g. views and materializedViews this.nodes = ImmutableMap.copyOf( catalogs.values().stream().flatMap( c -> c.getNodes().entrySet().stream() ).collect( Collectors.toMap( Entry::getKey, Entry::getValue, getDuplicateError() ) ) ); + this.collations = ImmutableMap.copyOf( catalogs.values().stream().flatMap( c -> c.getCollations().entrySet().stream() ).collect( Collectors.toMap( Entry::getKey, Entry::getValue, getDuplicateError() ) ) ); + this.views = buildViews(); this.connectedViews = buildConnectedViews(); @@ -526,6 +531,12 @@ public AlgNode getNodeInfo( long id ) { } + @Override + public AlgCollation getCollationInfo( long id ) { + return collations.get( id ); + } + + @Override public List getConnectedViews( long id ) { return connectedViews.get( id ); diff --git a/core/src/test/java/org/polypheny/db/snapshot/MockRelSnapshot.java b/core/src/test/java/org/polypheny/db/snapshot/MockRelSnapshot.java index a5de34af9b..df347cd5bd 100644 --- a/core/src/test/java/org/polypheny/db/snapshot/MockRelSnapshot.java +++ b/core/src/test/java/org/polypheny/db/snapshot/MockRelSnapshot.java @@ -21,6 +21,7 @@ import lombok.NonNull; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.polypheny.db.algebra.AlgCollation; import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.catalog.entity.LogicalConstraint; import org.polypheny.db.catalog.entity.logical.LogicalColumn; @@ -250,6 +251,12 @@ public AlgNode getNodeInfo( long id ) { } + @Override + public AlgCollation getCollationInfo( long id ) { + throw new UnsupportedOperationException(); + } + + @Override public List getConnectedViews( long id ) { throw new UnsupportedOperationException(); diff --git a/dbms/src/main/java/org/polypheny/db/view/MaterializedViewManagerImpl.java b/dbms/src/main/java/org/polypheny/db/view/MaterializedViewManagerImpl.java index 2e0631b705..e8ec5e5c59 100644 --- a/dbms/src/main/java/org/polypheny/db/view/MaterializedViewManagerImpl.java +++ b/dbms/src/main/java/org/polypheny/db/view/MaterializedViewManagerImpl.java @@ -313,7 +313,7 @@ public void addData( Transaction transaction, @Nullable List> store DataMigrator dataMigrator = transaction.getDataMigrator(); for ( AllocationEntity allocation : transaction.getSnapshot().alloc().getFromLogical( materializedView.id ) ) { Statement sourceStatement = transaction.createStatement(); - prepareSourceAlg( sourceStatement, materializedView.getAlgCollation(), algRoot.alg ); + prepareSourceAlg( sourceStatement, Catalog.snapshot().rel().getCollationInfo( materializedView.id ), algRoot.alg ); Statement targetStatement = transaction.createStatement(); if ( allocation.unwrap( AllocationTable.class ).isPresent() ) { diff --git a/dbms/src/test/java/org/polypheny/db/sql/view/BasicMaterializedViewTest.java b/dbms/src/test/java/org/polypheny/db/sql/view/BasicMaterializedViewTest.java index f91797db74..fbb28c8643 100644 --- a/dbms/src/test/java/org/polypheny/db/sql/view/BasicMaterializedViewTest.java +++ b/dbms/src/test/java/org/polypheny/db/sql/view/BasicMaterializedViewTest.java @@ -1312,4 +1312,44 @@ public void testMaterializedViewFromView() throws SQLException { } } + + @Test + public void testMaterializedViewRollback() throws SQLException { + try ( JdbcConnection polyphenyDbConnection = new JdbcConnection( false ) ) { + Connection connection = polyphenyDbConnection.getConnection(); + try ( Statement statement = connection.createStatement() ) { + statement.executeUpdate( VIEW_TEST_EMP_TABLE_SQL ); + + try { + statement.executeUpdate( "CREATE MATERIALIZED VIEW viewTestEmp AS SELECT * FROM viewTestEmpTable" ); + statement.executeUpdate( "CREATE MATERIALIZED VIEW viewTestEmp1 AS SELECT * FROM viewTestEmpTable" ); + + statement.executeUpdate( VIEW_TEST_EMP_TABLE_DATA_SQL ); + // Rollback the catalog to test that views are correctly restored + connection.rollback(); + + statement.executeUpdate( VIEW_TEST_EMP_TABLE_DATA_SQL ); + statement.executeUpdate( "ALTER MATERIALIZED VIEW viewTestEmp FRESHNESS MANUAL" ); + connection.commit(); + + TestHelper.checkResultSet( + statement.executeQuery( "SELECT empId,firstName,lastName,depId FROM viewTestEmp ORDER BY empid" ), + ImmutableList.of( + new Object[]{ 1, "Max", "Muster", 1 }, + new Object[]{ 2, "Ernst", "Walter", 2 }, + new Object[]{ 3, "Elsa", "Kuster", 3 } + ) + ); + + // Drop the materialized views + statement.executeUpdate( "DROP MATERIALIZED VIEW viewTestEmp" ); + statement.executeUpdate( "DROP MATERIALIZED VIEW viewTestEmp1" ); + } finally { + statement.executeUpdate( "DROP TABLE viewTestEmpTable" ); + dropTables( statement ); + } + } + } + } + } diff --git a/dbms/src/test/java/org/polypheny/db/sql/view/ViewTest.java b/dbms/src/test/java/org/polypheny/db/sql/view/ViewTest.java index 7d706db818..cfa184e1ee 100644 --- a/dbms/src/test/java/org/polypheny/db/sql/view/ViewTest.java +++ b/dbms/src/test/java/org/polypheny/db/sql/view/ViewTest.java @@ -438,4 +438,42 @@ public void testViewFromView() throws SQLException { } + @Test + public void testViewRollback() throws SQLException { + try ( JdbcConnection polyphenyDbConnection = new JdbcConnection( false ) ) { + Connection connection = polyphenyDbConnection.getConnection(); + try ( Statement statement = connection.createStatement() ) { + statement.executeUpdate( VIEW_TEST_EMP_TABLE_SQL ); + + try { + statement.executeUpdate( "CREATE VIEW viewTestEmp AS SELECT * FROM viewTestEmpTable" ); + statement.executeUpdate( "CREATE VIEW viewTestEmp1 AS SELECT * FROM viewTestEmpTable" ); + + statement.executeUpdate( VIEW_TEST_EMP_TABLE_DATA_SQL ); + // Rollback the catalog to test that views are correctly restored + connection.rollback(); + + statement.executeUpdate( VIEW_TEST_EMP_TABLE_DATA_SQL ); + connection.commit(); + + TestHelper.checkResultSet( + statement.executeQuery( "SELECT empId,firstName,lastName,depId FROM viewTestEmp ORDER BY empid" ), + ImmutableList.of( + new Object[]{ 1, "Max", "Muster", 1 }, + new Object[]{ 2, "Ernst", "Walter", 2 }, + new Object[]{ 3, "Elsa", "Kuster", 3 } + ) + ); + + // Drop the materialized views + statement.executeUpdate( "DROP VIEW viewTestEmp" ); + statement.executeUpdate( "DROP VIEW viewTestEmp1" ); + } finally { + statement.executeUpdate( "DROP TABLE viewTestEmpTable" ); + } + } + } + } + + } diff --git a/plugins/sql-language/src/main/java/org/polypheny/db/sql/language/ddl/SqlCreateView.java b/plugins/sql-language/src/main/java/org/polypheny/db/sql/language/ddl/SqlCreateView.java index 94d003ebb0..e9be690f49 100644 --- a/plugins/sql-language/src/main/java/org/polypheny/db/sql/language/ddl/SqlCreateView.java +++ b/plugins/sql-language/src/main/java/org/polypheny/db/sql/language/ddl/SqlCreateView.java @@ -112,12 +112,11 @@ public void execute( Context context, Statement statement, ParsedQueryContext pa PlacementType placementType = PlacementType.AUTOMATIC; - QueryLanguage language = QueryLanguage.from( "sql" ); - Processor sqlProcessor = statement.getTransaction().getProcessor( language ); + Processor sqlProcessor = statement.getTransaction().getProcessor( query.getLanguage() ); AlgRoot algRoot = sqlProcessor.translate( statement, ParsedQueryContext.builder() - .query( query.toString() ) - .language( language ) + .query( query.toSqlString( PolyphenyDbSqlDialect.DEFAULT ).toString() ) + .language( query.getLanguage() ) .queryNode( sqlProcessor.validate( statement.getTransaction(), this.query, RuntimeConfig.ADD_DEFAULT_VALUES_IN_INSERTS.getBoolean() ).left )