Skip to content

Commit

Permalink
correctly added foreign key constraint on table create
Browse files Browse the repository at this point in the history
  • Loading branch information
datomo committed Dec 2, 2023
1 parent 7357987 commit 4c54dfd
Show file tree
Hide file tree
Showing 13 changed files with 269 additions and 1,938 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@

package org.polypheny.db.catalog.logistic;

import lombok.Getter;
import lombok.NonNull;
import org.polypheny.db.catalog.exceptions.GenericRuntimeException;

@Getter
public enum ConstraintType {
UNIQUE( 1 ),
PRIMARY( 2 );
PRIMARY( 2 ),

FOREIGN( 3 );

private final int id;

Expand All @@ -30,25 +35,20 @@ public enum ConstraintType {
}


public int getId() {
return id;
}


public static ConstraintType getById( int id ) {
for ( ConstraintType e : values() ) {
if ( e.id == id ) {
return e;
}
}
throw new RuntimeException( "Unknown ConstraintType with id: " + id );
throw new GenericRuntimeException( "Unknown ConstraintType with id: " + id );
}


public static ConstraintType parse( @NonNull String str ) {
if ( str.equalsIgnoreCase( "UNIQUE" ) ) {
return ConstraintType.UNIQUE;
}
throw new RuntimeException( "Unknown ConstraintType with name: " + str );
throw new GenericRuntimeException( "Unknown ConstraintType with name: " + str );
}
}
72 changes: 43 additions & 29 deletions core/src/main/java/org/polypheny/db/ddl/DdlManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import lombok.Value;
import org.polypheny.db.adapter.DataStore;
import org.polypheny.db.adapter.DeployMode;
import org.polypheny.db.algebra.AlgCollation;
Expand Down Expand Up @@ -465,13 +466,12 @@ public static DdlManager getInstance() {
/**
* Adds a new constraint to a table
*
* @param information
* @param namespaceId
* @param constraintName the name of the constraint
* @param constraintType the type of the constraint
* @param columnIds the ids of the columns for which to create the constraint
* @param tableId the id of the table
* @param columnIds
* @param tableId
*/
public abstract void createConstraint( long namespaceId, String constraintName, ConstraintType constraintType, List<Long> columnIds, long tableId );
public abstract void createConstraint( ConstraintInformation information, long namespaceId, List<Long> columnIds, long tableId );

/**
* Drop a NAMESPACE
Expand Down Expand Up @@ -559,13 +559,14 @@ public static DdlManager getInstance() {
* Helper class which holds all information required for creating a column,
* decoupled from a specific query language
*/
@Value
public static class FieldInformation {

public final String name;
public final ColumnTypeInformation typeInformation;
public final Collation collation;
public final PolyValue defaultValue;
public final int position;
public String name;
public ColumnTypeInformation typeInformation;
public Collation collation;
public PolyValue defaultValue;
public int position;


public FieldInformation( String name, ColumnTypeInformation typeInformation, Collation collation, PolyValue defaultValue, int position ) {
Expand All @@ -583,17 +584,28 @@ public FieldInformation( String name, ColumnTypeInformation typeInformation, Col
* Helper class which holds all information required for creating a constraint,
* decoupled from its query language
*/

@Value
public static class ConstraintInformation {

public final String name;
public final ConstraintType type;
public final List<String> columnNames;
public String name;
public ConstraintType type;
public List<String> columnNames;
public @Nullable String foreignKeyTable;
public @Nullable String foreignKeyColumnName;


public ConstraintInformation( String name, ConstraintType type, List<String> columnNames ) {
public ConstraintInformation( String name, ConstraintType type, List<String> columnNames, String foreignKeyTable, String foreignKeyColumnName ) {
this.name = name;
this.type = type;
this.columnNames = columnNames;
this.foreignKeyTable = foreignKeyTable;
this.foreignKeyColumnName = foreignKeyColumnName;
}


public ConstraintInformation( String name, ConstraintType type, List<String> columnNames ) {
this( name, type, columnNames, null, null );
}

}
Expand All @@ -603,15 +615,16 @@ public ConstraintInformation( String name, ConstraintType type, List<String> col
* Helper class, which holds all type information for a column
* decoupled from the used query language
*/
@Value
public static class ColumnTypeInformation {

public final PolyType type;
public final PolyType collectionType;
public final Integer precision;
public final Integer scale;
public final Integer dimension;
public final Integer cardinality;
public final Boolean nullable;
public PolyType type;
public PolyType collectionType;
public Integer precision;
public Integer scale;
public Integer dimension;
public Integer cardinality;
public Boolean nullable;


public ColumnTypeInformation(
Expand Down Expand Up @@ -646,16 +659,17 @@ public static ColumnTypeInformation fromDataTypeSpec( DataTypeSpec sqlDataType )
}


@Value
public static class PartitionInformation {

public final LogicalTable table;
public final String columnName;
public final String typeName;
public final List<String> partitionGroupNames;
public final int numberOfPartitionGroups;
public final int numberOfPartitions;
public final List<List<String>> qualifiers;
public final RawPartitionInformation rawPartitionInformation;
public LogicalTable table;
public String columnName;
public String typeName;
public List<String> partitionGroupNames;
public int numberOfPartitionGroups;
public int numberOfPartitions;
public List<List<String>> qualifiers;
public RawPartitionInformation rawPartitionInformation;


public PartitionInformation(
Expand Down
52 changes: 39 additions & 13 deletions dbms/src/main/java/org/polypheny/db/ddl/DdlManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -507,12 +507,12 @@ public void createForeignKey( LogicalTable table, LogicalTable refTable, List<St
checkIfDdlPossible( table.entityType );
checkIfDdlPossible( refTable.entityType );

List<Long> columnIds = new LinkedList<>();
List<Long> columnIds = new ArrayList<>();
for ( String columnName : columnNames ) {
LogicalColumn logicalColumn = catalog.getSnapshot().rel().getColumn( table.id, columnName ).orElseThrow();
columnIds.add( logicalColumn.id );
}
List<Long> referencesIds = new LinkedList<>();
List<Long> referencesIds = new ArrayList<>();
for ( String columnName : refColumnNames ) {
LogicalColumn logicalColumn = catalog.getSnapshot().rel().getColumn( refTable.id, columnName ).orElseThrow();
referencesIds.add( logicalColumn.id );
Expand Down Expand Up @@ -737,6 +737,7 @@ public void createAllocationPlacement( LogicalTable table, List<LogicalColumn> n

}


@Override
public void createPrimaryKey( LogicalTable table, List<String> columnNames, Statement statement ) {
// Make sure that this is a table of type TABLE (and not SOURCE)
Expand Down Expand Up @@ -1301,7 +1302,6 @@ public void modifyPartitionPlacement( LogicalTable table, List<Long> partitionId
List<AllocationEntity> removedPartitions = currentAllocs.stream().filter( a -> !partitionIds.contains( a.partitionId ) ).collect( Collectors.toList() );
List<Long> addedPartitions = partitionIds.stream().filter( id -> !currentPartitionsId.contains( id ) ).collect( Collectors.toList() );


// Copy the data to the newly added column placements
DataMigrator dataMigrator = statement.getTransaction().getDataMigrator();
List<AllocationColumn> allocationColumns = catalog.getSnapshot().alloc().getColumns( placement.id );
Expand Down Expand Up @@ -1915,9 +1915,10 @@ public void createTable( long namespaceId, String name, List<FieldInformation> f

List<Long> pkIds = new ArrayList<>();

for ( ConstraintInformation constraint : constraints ) {
// create foreign keys later on
for ( ConstraintInformation constraint : constraints.stream().filter( c -> c.getType() != ConstraintType.FOREIGN ).collect( Collectors.toList() ) ) {
List<Long> columnIds = constraint.columnNames.stream().map( key -> ids.get( key ).id ).collect( Collectors.toList() );
createConstraint( namespaceId, constraint.name, constraint.type, columnIds, logical.id );
createConstraint( constraint, namespaceId, columnIds, logical.id );

if ( constraint.type == ConstraintType.PRIMARY ) {
pkIds = columnIds;
Expand All @@ -1936,6 +1937,13 @@ public void createTable( long namespaceId, String name, List<FieldInformation> f
}

catalog.updateSnapshot();

constraints.stream().filter( c -> c.getType() == ConstraintType.FOREIGN ).forEach( c -> {
List<Long> columnIds = c.columnNames.stream().map( key -> ids.get( key ).id ).collect( Collectors.toList() );
createConstraint( c, namespaceId, columnIds, logical.id );
} );

catalog.updateSnapshot();
}


Expand Down Expand Up @@ -2599,14 +2607,32 @@ private LogicalColumn addColumn( long namespaceId, String columnName, ColumnType


@Override
public void createConstraint( long namespaceId, String constraintName, ConstraintType constraintType, List<Long> columnIds, long tableId ) {
if ( constraintType == ConstraintType.PRIMARY ) {
catalog.getLogicalRel( namespaceId ).addPrimaryKey( tableId, columnIds );
} else if ( constraintType == ConstraintType.UNIQUE ) {
if ( constraintName == null ) {
constraintName = NameGenerator.generateConstraintName();
}
catalog.getLogicalRel( namespaceId ).addUniqueConstraint( tableId, constraintName, columnIds );
public void createConstraint( ConstraintInformation information, long namespaceId, List<Long> columnIds, long tableId ) {
String constraintName = information.name;
if ( constraintName == null ) {
constraintName = NameGenerator.generateConstraintName();
}
switch ( information.getType() ) {
case UNIQUE:
catalog.getLogicalRel( namespaceId ).addUniqueConstraint( tableId, constraintName, columnIds );
break;
case PRIMARY:
catalog.getLogicalRel( namespaceId ).addPrimaryKey( tableId, columnIds );
break;
case FOREIGN:
String foreignKeyTable = information.foreignKeyTable;
long foreignTableId;
if ( foreignKeyTable.split( "\\." ).length == 1 ) {
foreignTableId = catalog.getSnapshot().rel().getTable( namespaceId, foreignKeyTable ).orElseThrow().id;
} else if ( foreignKeyTable.split( "\\." ).length == 2 ) {
foreignTableId = catalog.getSnapshot().rel().getTable( foreignKeyTable.split( "\\." )[0], foreignKeyTable.split( "\\." )[1] ).orElseThrow().id;
} else {
throw new GenericRuntimeException( "Invalid foreign key table name" );
}
long columnId = catalog.getSnapshot().rel().getColumn( foreignTableId, information.foreignKeyColumnName ).orElseThrow().id;
catalog.getLogicalRel( namespaceId ).addForeignKey( tableId, columnIds, foreignTableId, List.of( columnId ), constraintName, ForeignKeyOption.NONE, ForeignKeyOption.NONE );

break;
}
}

Expand Down
64 changes: 64 additions & 0 deletions dbms/src/test/java/org/polypheny/db/catalog/RestoreTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2019-2023 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.catalog;

import org.junit.BeforeClass;
import org.junit.Test;
import org.polypheny.db.TestHelper;
import org.polypheny.db.catalog.entity.logical.LogicalTable;

@SuppressWarnings({ "SqlDialectInspection", "SqlNoDataSourceInspection" })
public class RestoreTest {


private final String CONSTRAINT_CREATE = "CREATE TABLE IF NOT EXISTS constraint_test ("
+ "ctid INTEGER NOT NULL, "
+ "a INTEGER NOT NULL, "
+ "b INTEGER NOT NULL, "
+ "c INTEGER NOT NULL, "
+ "PRIMARY KEY (ctid), "
+ "CONSTRAINT u_a UNIQUE (a), "
+ "CONSTRAINT u_ab UNIQUE (a,b))";
private final String CONSTRAINT2_CREATE = "CREATE TABLE IF NOT EXISTS constraint_test2 ("
+ "ctid INTEGER NOT NULL, "
+ "a INTEGER NOT NULL, "
+ "b INTEGER NOT NULL, "
+ "c INTEGER NOT NULL, "
+ "PRIMARY KEY (ctid), "
+ "FOREIGN KEY (a) REFERENCES public.constraint_test(a))";


@BeforeClass
public static void setUp() {
//noinspection ResultOfMethodCallIgnored
TestHelper.getInstance();
}


@Test
public void foreignTableKeyTest() {
TestHelper.executeSql(
( c, s ) -> s.executeUpdate( CONSTRAINT_CREATE ),
( c, s ) -> s.executeUpdate( CONSTRAINT2_CREATE ),
( c, s ) -> c.commit()
);
LogicalTable table = Catalog.snapshot().rel().getTable( "public", "constraint_test2" ).orElseThrow();
assert Catalog.snapshot().rel().getForeignKeys( table.id ).size() == 1;
}


}
15 changes: 15 additions & 0 deletions plugins/sql-language/src/main/codegen/Parser.jj
Original file line number Diff line number Diff line change
Expand Up @@ -1933,6 +1933,8 @@ void TableElement(List<SqlNode> list) :
final Span s = Span.of();
final ColumnStrategy strategy;
final String collation;
SqlIdentifier refTable;
SqlIdentifier refColumn;
}
{
id = SimpleIdentifier()
Expand Down Expand Up @@ -2009,6 +2011,19 @@ void TableElement(List<SqlNode> list) :
columnList = ParenthesizedSimpleIdentifierList() {
list.add(SqlDdlNodes.primary(s.end(columnList), name, columnList));
}
|
<FOREIGN> { s.add(this); }
<KEY>
columnList = ParenthesizedSimpleIdentifierList()
<REFERENCES> {
s.add(this);
}
refTable = CompoundIdentifier()
<LPAREN>
refColumn = SimpleIdentifier()
<RPAREN> {
list.add(SqlDdlNodes.foreign(s.end(this), name, columnList, refTable, refColumn));
}
)
}

Expand Down

This file was deleted.

Loading

0 comments on commit 4c54dfd

Please sign in to comment.