Skip to content

Commit

Permalink
Fix relational inserts without column names
Browse files Browse the repository at this point in the history
  • Loading branch information
Tobias Hafner committed Dec 7, 2024
1 parent f1aa0ae commit 2bde60b
Show file tree
Hide file tree
Showing 14 changed files with 249 additions and 188 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/org/polypheny/db/adapter/Scannable.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ static PhysicalEntity createSubstitutionEntity( Scannable scannable, Context con

int i = 0;
for ( ColumnContext col : columnsInformations ) {
LogicalColumn column = new LogicalColumn( builder.getNewFieldId(), col.name, table.id, table.namespaceId, i, col.type, null, col.precision, null, null, null, col.nullable, Collation.getDefaultCollation(), null );
LogicalColumn column = new LogicalColumn( builder.getNewFieldId(), false, col.name, table.id, table.namespaceId, i, col.type, null, col.precision, null, null, null, col.nullable, Collation.getDefaultCollation(), null );
columns.add( column );
i++;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,21 @@ public interface LogicalRelationalCatalog extends LogicalCatalog {
*/
LogicalColumn addColumn( String name, long tableId, int position, PolyType type, PolyType collectionsType, Integer length, Integer scale, Integer dimension, Integer cardinality, boolean nullable, Collation collation );

/**
* Adds a column.
*
* @param name The name of the column
* @param tableId The id of the corresponding table
* @param position The ordinal position of the column (starting with 1)
* @param type The type of the column
* @param length The length of the field (if applicable, else null)
* @param scale The number of digits after the decimal point (if applicable, else null)
* @param nullable Weather the column can contain null values
* @param collation The collation of the field (if applicable, else null)
* @param isIdentifier Specifies weather the colum contains identifiers used for MVCC
* @return The id of the inserted column
*/
LogicalColumn addColumn( String name, long tableId, int position, PolyType type, PolyType collectionsType, Integer length, Integer scale, Integer dimension, Integer cardinality, boolean nullable, Collation collation, boolean isIdentifier );

/**
* Renames a column
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,15 @@ public AlgDataType getTupleType() {
};
}

public AlgDataType getTupleType(boolean hideIdentifier) {
return switch ( dataModel ) {
case RELATIONAL -> throw new UnsupportedOperationException( "Should be overwritten by child" );
//TODO TH: adjust where necessary
case DOCUMENT -> DocumentType.ofId();
case GRAPH -> GraphType.of();
};
}


@Override
public AlgDataType getTupleType( AlgDataTypeFactory typeFactory ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ public class LogicalColumn implements PolyObject, Comparable<LogicalColumn> {
@JsonProperty
public long id;

@Serialize
@JsonProperty
public boolean isIdentifier;

@Serialize
@JsonProperty
public String name;
Expand Down Expand Up @@ -105,6 +109,7 @@ public class LogicalColumn implements PolyObject, Comparable<LogicalColumn> {

public LogicalColumn(
@Deserialize("id") final long id,
@Deserialize("isIdentifier") final boolean isIdentifier,
@Deserialize("name") @NonNull final String name,
@Deserialize("tableId") final long tableId,
@Deserialize("namespaceId") final long namespaceId,
Expand All @@ -119,6 +124,7 @@ public LogicalColumn(
@Deserialize("collation") final Collation collation,
@Deserialize("defaultValue") final LogicalDefaultValue defaultValue ) {
this.id = id;
this.isIdentifier = isIdentifier;
this.name = name;
this.tableId = tableId;
this.namespaceId = namespaceId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
import io.activej.serializer.annotations.SerializeNullable;
import java.io.Serial;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
Expand All @@ -40,7 +42,7 @@
import org.polypheny.db.catalog.logistic.DataModel;
import org.polypheny.db.catalog.logistic.EntityType;
import org.polypheny.db.schema.ColumnStrategy;
import org.polypheny.db.transaction.locking.Lockable;
import org.polypheny.db.transaction.locking.IdentifierUtils;

@EqualsAndHashCode(callSuper = false)
@SuperBuilder(toBuilder = true)
Expand Down Expand Up @@ -75,9 +77,15 @@ public LogicalTable(

@Override
public AlgDataType getTupleType() {
return getTupleType( false );
}


@Override
public AlgDataType getTupleType( boolean hideIdentifier ) {
final AlgDataTypeFactory.Builder fieldInfo = AlgDataTypeFactory.DEFAULT.builder();

for ( LogicalColumn column : Catalog.snapshot().rel().getColumns( id ).stream().sorted( Comparator.comparingInt( a -> a.position ) ).toList() ) {
for ( LogicalColumn column : getColumns(hideIdentifier).stream().sorted( Comparator.comparingInt( a -> a.position ) ).toList() ) {
AlgDataType sqlType = column.getAlgDataType( AlgDataTypeFactory.DEFAULT );
fieldInfo.add( column.id, column.name, null, sqlType ).nullable( column.nullable );
}
Expand All @@ -93,11 +101,23 @@ public Expression asExpression() {


public List<ColumnStrategy> getColumnStrategies() {
return getColumns().stream().map( c -> c.nullable ? ColumnStrategy.NULLABLE : ColumnStrategy.NOT_NULLABLE ).toList();
return getColumnStrategies(false);
}

public List<ColumnStrategy> getColumnStrategies(boolean hideIdentifiers) {
return getColumns(hideIdentifiers).stream().map( c -> c.nullable ? ColumnStrategy.NULLABLE : ColumnStrategy.NOT_NULLABLE ).toList();
}


public List<LogicalColumn> getColumns() {
return getColumns( false );
}


public List<LogicalColumn> getColumns( boolean hideIdentifier ) {
if ( hideIdentifier ) {
return Catalog.snapshot().rel().getColumns( id ).stream().filter( c -> !c.isIdentifier() ).toList();
}
return Catalog.snapshot().rel().getColumns( id );
}

Expand All @@ -112,6 +132,11 @@ public List<String> getColumnNames() {
}


public List<String> getColumnNames(boolean hideIdentifier) {
return getColumns(hideIdentifier).stream().map( c -> c.name ).toList();
}


@Override
public String getNamespaceName() {
return Catalog.snapshot().getNamespace( namespaceId ).orElseThrow().name;
Expand Down Expand Up @@ -159,5 +184,4 @@ public String toString() {
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -290,11 +290,19 @@ public void deleteKey( long id ) {
change( CatalogEvent.KEY_DROPPED, id, null );
}

@Override
public LogicalColumn addColumn( String name, long tableId, int position, PolyType type, PolyType collectionsType, Integer length, Integer scale, Integer dimension, Integer cardinality, boolean nullable, Collation collation) {
long id = idBuilder.getNewFieldId();
LogicalColumn column = new LogicalColumn( id, false, name, tableId, logicalNamespace.id, position, type, collectionsType, length, scale, dimension, cardinality, nullable, collation, null );
columns.put( id, column );
change( CatalogEvent.LOGICAL_REL_FIELD_CREATED, null, id );
return column;
}

@Override
public LogicalColumn addColumn( String name, long tableId, int position, PolyType type, PolyType collectionsType, Integer length, Integer scale, Integer dimension, Integer cardinality, boolean nullable, Collation collation ) {
public LogicalColumn addColumn( String name, long tableId, int position, PolyType type, PolyType collectionsType, Integer length, Integer scale, Integer dimension, Integer cardinality, boolean nullable, Collation collation, boolean isIdentifier ) {
long id = idBuilder.getNewFieldId();
LogicalColumn column = new LogicalColumn( id, name, tableId, logicalNamespace.id, position, type, collectionsType, length, scale, dimension, cardinality, nullable, collation, null );
LogicalColumn column = new LogicalColumn( id, isIdentifier, name, tableId, logicalNamespace.id, position, type, collectionsType, length, scale, dimension, cardinality, nullable, collation, null );
columns.put( id, column );
change( CatalogEvent.LOGICAL_REL_FIELD_CREATED, null, id );
return column;
Expand Down
6 changes: 1 addition & 5 deletions core/src/main/java/org/polypheny/db/rex/RexLiteral.java
Original file line number Diff line number Diff line change
Expand Up @@ -441,11 +441,7 @@ private static void printAsJava( PolyValue value, PrintWriter pw, PolyType typeN
pw.print( value.asBoolean().value );
break;
case DECIMAL:
assert value.isBigDecimal() || value.isLong();
if (value.isLong()) {
pw.print( value.asLong().value );
break;
}
assert value.isBigDecimal();
pw.print( value.asBigDecimal().value );
break;
case DOUBLE:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.polypheny.db.transaction.locking;

import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -58,19 +59,14 @@
import org.polypheny.db.algebra.logical.relational.LogicalRelTableFunctionScan;
import org.polypheny.db.algebra.logical.relational.LogicalRelUnion;
import org.polypheny.db.algebra.logical.relational.LogicalRelValues;
import org.polypheny.db.algebra.type.AlgDataTypeFactoryImpl;
import org.polypheny.db.algebra.type.AlgDataType;
import org.polypheny.db.algebra.type.AlgDataTypeField;
import org.polypheny.db.rex.RexIndexRef;
import org.polypheny.db.rex.RexLiteral;
import org.polypheny.db.rex.RexNode;
import org.polypheny.db.type.PolyType;
import org.polypheny.db.type.entity.PolyValue;
import org.polypheny.db.type.entity.numerical.PolyLong;

public class IdentifierAdder extends AlgShuttleImpl {

private static final PolyLong missingIdentifier = new PolyLong( IdentifierUtils.MISSING_IDENTIFIER );


public static AlgRoot process( AlgRoot root ) {
return root.withAlg( root.alg.accept( new IdentifierAdder() ) );
Expand Down Expand Up @@ -103,27 +99,7 @@ public AlgNode visit( LogicalRelTableFunctionScan scan ) {

@Override
public AlgNode visit( LogicalRelValues values ) {
ImmutableList<ImmutableList<RexLiteral>> immutableValues = values.tuples.stream()
.map( row -> {
RexLiteral identifierLiteral = row.get( 0 );
if ( !identifierLiteral.getValue().equals( missingIdentifier ) ) {
/*
This method is only called on the top level of an insert.
The values present are thus all inserted by the user.
If identifiers are present at this stage, they were added by the user.
*/
IdentifierUtils.throwIllegalFieldName();
}
PolyValue identifier = IdentifierUtils.getIdentifier();
return ImmutableList.<RexLiteral>builder()
.add( new RexLiteral( identifier, identifierLiteral.getType(), PolyType.DECIMAL ) )
.addAll( row.subList( 1, row.size() ) )
.build();

} )
.collect( ImmutableList.toImmutableList() );

return LogicalRelValues.create( values.getCluster(), values.getRowType(), immutableValues );
return values;
}


Expand All @@ -134,87 +110,68 @@ public AlgNode visit( LogicalRelFilter filter ) {


@Override
public AlgNode visit(LogicalRelProject project) {
public AlgNode visit( LogicalRelProject project ) {
/*
Projects at this stage never contain references to values for the identifier column.
New identifiers must thus be drawn and the projection adjusted accordingly.
*/

// Detect underpopulated columns if the user specifies fewer values than there are columns in the table
Optional<Integer> indexOpt = findIdentifierIndex(project);
if (indexOpt.isEmpty()) {
Optional<Integer> identifier = findIdentifierIndex( project );
if ( identifier.isEmpty() ) {
return project;
}

int index = indexOpt.get();
int identifierIndex = identifier.get();
AlgNode input = project.getInput();
if (!(input instanceof LogicalRelValues inputValues)) {
return project;
}

if ( hasLessInputsThanColumns(project, inputValues)) {
return createNewProjectWithIdentifiers(project, inputValues, index);
if ( input instanceof LogicalRelValues inputValues ) {
return createNewProjectWithIdentifiers( project, inputValues, identifierIndex );
}

return visitChild(project, 0, project.getInput());
}

private Optional<Integer> findIdentifierIndex(LogicalRelProject project) {

private Optional<Integer> findIdentifierIndex( LogicalRelProject project ) {
return project.getRowType().getFields().stream()
.filter(field -> field.getName().equals(IdentifierUtils.IDENTIFIER_KEY))
.map(AlgDataTypeField::getIndex)
.filter( field -> field.getName().equals( IdentifierUtils.IDENTIFIER_KEY ) )
.map( AlgDataTypeField::getIndex )
.findFirst();
}

private boolean hasLessInputsThanColumns(LogicalRelProject project, LogicalRelValues inputValues) {
long fieldCount = project.getRowType().getFieldCount();
long inputFieldCount = inputValues.tuples.get(0).size();
return inputFieldCount < fieldCount;
}

private AlgNode createNewProjectWithIdentifiers(LogicalRelProject project, LogicalRelValues inputValues, int index) {
ImmutableList<ImmutableList<RexLiteral>> immutableValues = adjustInputValues(inputValues, index);
private AlgNode createNewProjectWithIdentifiers( LogicalRelProject project, LogicalRelValues inputValues, int identifierIndex ) {
ImmutableList<ImmutableList<RexLiteral>> newValues = adjustInputValues( inputValues, identifierIndex );
LogicalRelValues newInput = new LogicalRelValues(
inputValues.getCluster(),
inputValues.getTraitSet(),
inputValues.getRowType(),
immutableValues
project.getRowType(),
newValues
);

List<RexNode> newProjects = createNewProjects(project, inputValues);
return project.copy(project.getTraitSet(), newInput, newProjects, project.getRowType());
List<RexNode> newProjects = createNewProjects( project, newInput );
return project.copy( project.getTraitSet(), newInput, newProjects, project.getRowType() );
}

private ImmutableList<ImmutableList<RexLiteral>> adjustInputValues(LogicalRelValues inputValues, int index) {

private ImmutableList<ImmutableList<RexLiteral>> adjustInputValues( LogicalRelValues inputValues, int identifierIndex ) {
return inputValues.tuples.stream()
.map(row -> {
PolyValue identifier = IdentifierUtils.getIdentifier();
ImmutableList.Builder<RexLiteral> builder = ImmutableList.builder();
builder.addAll(row.subList(0, index));
builder.add(new RexLiteral(
identifier,
AlgDataTypeFactoryImpl.DEFAULT.createPolyType(identifier.getType()),
PolyType.DECIMAL
));
builder.addAll(row.subList(index, row.size()));
return builder.build();
})
.collect(ImmutableList.toImmutableList());
.map( row -> ImmutableList.<RexLiteral>builder()
.addAll( row.subList( 0, identifierIndex ) )
.add( IdentifierUtils.getIdentifierAsLiteral() )
.addAll( row.subList( identifierIndex, row.size() ) )
.build() )
.collect( ImmutableList.toImmutableList() );
}


private List<RexNode> createNewProjects(LogicalRelProject project, LogicalRelValues inputValues) {
List<RexNode> newProjects = new LinkedList<>();
long fieldCount = project.getRowType().getFieldCount();
long inputFieldCount = inputValues.tuples.get(0).size();

for (int position = 0; position < fieldCount; position++) {
if (position < inputFieldCount) {
newProjects.add(new RexIndexRef(
position,
project.getRowType().getFields().get(position).getType()
));
} else {
newProjects.add(new RexLiteral(
null,
project.getRowType().getFields().get(position).getType(),
project.getRowType().getFields().get(position).getType().getPolyType()
));
}
List<RexNode> newProjects = new ArrayList<>();
List<AlgDataTypeField> fields = project.getRowType().getFields();
int inputFieldCount = inputValues.tuples.get(0).size();

for (int position = 0; position < fields.size(); position++) {
AlgDataType fieldType = fields.get(position).getType();
newProjects.add(position < inputFieldCount
? new RexIndexRef(position, fieldType)
: new RexLiteral(null, fieldType, fieldType.getPolyType()));
}

return newProjects;
Expand Down Expand Up @@ -271,18 +228,7 @@ public AlgNode visit( LogicalConditionalExecute lce ) {

@Override
public AlgNode visit( LogicalRelModify modify ) {
switch ( modify.getOperation() ) {
case UPDATE -> {
if ( modify.getUpdateColumns().contains( IdentifierUtils.IDENTIFIER_KEY ) ) {
IdentifierUtils.throwIllegalFieldName();
}
return modify;
}
case INSERT -> {
return visitChildren( modify );
}
}
return modify;
return visitChildren( modify );
}


Expand Down
Loading

0 comments on commit 2bde60b

Please sign in to comment.