From f1aa0ae95d3ee34692317152d6bdd5efe9d97cab Mon Sep 17 00:00:00 2001 From: Tobias Hafner Date: Fri, 6 Dec 2024 16:48:36 +0100 Subject: [PATCH] Start query rewriting on alg nodes --- .../db/algebra/core/relational/RelModify.java | 31 +- .../db/languages/LanguageManager.java | 2 + .../transaction/locking/IdentifierAdder.java | 409 ++++++++++++++++++ .../locking/IdentifierRegistry.java | 2 +- .../transaction/locking/IdentifierUtils.java | 56 +-- .../locking/IdentifierRegistryTest.java | 22 +- .../org/polypheny/db/ddl/DdlManagerImpl.java | 10 +- .../db/transaction/EntityIdentifierTests.java | 343 ++++++--------- 8 files changed, 578 insertions(+), 297 deletions(-) create mode 100644 core/src/main/java/org/polypheny/db/transaction/locking/IdentifierAdder.java diff --git a/core/src/main/java/org/polypheny/db/algebra/core/relational/RelModify.java b/core/src/main/java/org/polypheny/db/algebra/core/relational/RelModify.java index 775b78f633..2a50f3424e 100644 --- a/core/src/main/java/org/polypheny/db/algebra/core/relational/RelModify.java +++ b/core/src/main/java/org/polypheny/db/algebra/core/relational/RelModify.java @@ -26,7 +26,6 @@ import org.polypheny.db.algebra.AlgWriter; import org.polypheny.db.algebra.constant.Kind; import org.polypheny.db.algebra.core.common.Modify; -import org.polypheny.db.algebra.logical.relational.LogicalRelModify; import org.polypheny.db.algebra.logical.relational.LogicalRelValues; import org.polypheny.db.algebra.metadata.AlgMetadataQuery; import org.polypheny.db.algebra.type.AlgDataType; @@ -105,33 +104,17 @@ protected RelModify( this.operation = operation; this.updateColumns = updateColumns; this.sourceExpressions = sourceExpressions; - switch(operation) { - case UPDATE -> { - Objects.requireNonNull( updateColumns ); - Objects.requireNonNull( sourceExpressions ); - Preconditions.checkArgument( sourceExpressions.size() == updateColumns.size() ); - } - case INSERT -> { - Preconditions.checkArgument( updateColumns == null ); - Preconditions.checkArgument( sourceExpressions == null ); - addIdentifiers(); - } - default -> { - Preconditions.checkArgument( updateColumns == null ); - Preconditions.checkArgument( sourceExpressions == null ); - } + if (operation == Operation.UPDATE) { + Objects.requireNonNull( updateColumns ); + Objects.requireNonNull( sourceExpressions ); + Preconditions.checkArgument( sourceExpressions.size() == updateColumns.size() ); + } else { + Preconditions.checkArgument( updateColumns == null ); + Preconditions.checkArgument( sourceExpressions == null ); } this.flattened = flattened; } - private void addIdentifiers() { - if (!(input instanceof LogicalRelValues values) ) { - LOGGER.warn("New source type detected: {}", input); - return; - } - input = IdentifierUtils.overwriteIdentifierInInput( values ); - } - public boolean isInsert() { return operation == Operation.INSERT; diff --git a/core/src/main/java/org/polypheny/db/languages/LanguageManager.java b/core/src/main/java/org/polypheny/db/languages/LanguageManager.java index dc47b543d0..7b1663fb0c 100644 --- a/core/src/main/java/org/polypheny/db/languages/LanguageManager.java +++ b/core/src/main/java/org/polypheny/db/languages/LanguageManager.java @@ -39,6 +39,7 @@ import org.polypheny.db.processing.QueryContext.ParsedQueryContext; import org.polypheny.db.transaction.Statement; import org.polypheny.db.transaction.Transaction; +import org.polypheny.db.transaction.locking.IdentifierAdder; import org.polypheny.db.util.DeadlockException; import org.polypheny.db.util.Pair; @@ -184,6 +185,7 @@ public List anyPrepareQuery( QueryContext context, Statem } AlgRoot root = processor.translate( statement, parsed ); + root = IdentifierAdder.process( root ); if ( transaction.isAnalyze() ) { statement.getOverviewDuration().stop( "Translation" ); diff --git a/core/src/main/java/org/polypheny/db/transaction/locking/IdentifierAdder.java b/core/src/main/java/org/polypheny/db/transaction/locking/IdentifierAdder.java new file mode 100644 index 0000000000..ca1b330f13 --- /dev/null +++ b/core/src/main/java/org/polypheny/db/transaction/locking/IdentifierAdder.java @@ -0,0 +1,409 @@ +/* + * 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.transaction.locking; + +import com.google.common.collect.ImmutableList; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import org.polypheny.db.algebra.AlgNode; +import org.polypheny.db.algebra.AlgRoot; +import org.polypheny.db.algebra.AlgShuttleImpl; +import org.polypheny.db.algebra.logical.common.LogicalConditionalExecute; +import org.polypheny.db.algebra.logical.common.LogicalConstraintEnforcer; +import org.polypheny.db.algebra.logical.document.LogicalDocumentAggregate; +import org.polypheny.db.algebra.logical.document.LogicalDocumentFilter; +import org.polypheny.db.algebra.logical.document.LogicalDocumentModify; +import org.polypheny.db.algebra.logical.document.LogicalDocumentProject; +import org.polypheny.db.algebra.logical.document.LogicalDocumentScan; +import org.polypheny.db.algebra.logical.document.LogicalDocumentSort; +import org.polypheny.db.algebra.logical.document.LogicalDocumentTransformer; +import org.polypheny.db.algebra.logical.document.LogicalDocumentValues; +import org.polypheny.db.algebra.logical.lpg.LogicalLpgAggregate; +import org.polypheny.db.algebra.logical.lpg.LogicalLpgFilter; +import org.polypheny.db.algebra.logical.lpg.LogicalLpgMatch; +import org.polypheny.db.algebra.logical.lpg.LogicalLpgModify; +import org.polypheny.db.algebra.logical.lpg.LogicalLpgProject; +import org.polypheny.db.algebra.logical.lpg.LogicalLpgScan; +import org.polypheny.db.algebra.logical.lpg.LogicalLpgSort; +import org.polypheny.db.algebra.logical.lpg.LogicalLpgTransformer; +import org.polypheny.db.algebra.logical.lpg.LogicalLpgUnwind; +import org.polypheny.db.algebra.logical.lpg.LogicalLpgValues; +import org.polypheny.db.algebra.logical.relational.LogicalRelAggregate; +import org.polypheny.db.algebra.logical.relational.LogicalRelCorrelate; +import org.polypheny.db.algebra.logical.relational.LogicalRelExchange; +import org.polypheny.db.algebra.logical.relational.LogicalRelFilter; +import org.polypheny.db.algebra.logical.relational.LogicalRelIntersect; +import org.polypheny.db.algebra.logical.relational.LogicalRelJoin; +import org.polypheny.db.algebra.logical.relational.LogicalRelMatch; +import org.polypheny.db.algebra.logical.relational.LogicalRelMinus; +import org.polypheny.db.algebra.logical.relational.LogicalRelModify; +import org.polypheny.db.algebra.logical.relational.LogicalRelProject; +import org.polypheny.db.algebra.logical.relational.LogicalRelScan; +import org.polypheny.db.algebra.logical.relational.LogicalRelSort; +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.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() ) ); + } + + + @Override + public AlgNode visit( LogicalRelAggregate aggregate ) { + return visitChild( aggregate, 0, aggregate.getInput() ); + } + + + @Override + public AlgNode visit( LogicalRelMatch match ) { + return visitChild( match, 0, match.getInput() ); + } + + + @Override + public AlgNode visit( LogicalRelScan scan ) { + return scan; + } + + + @Override + public AlgNode visit( LogicalRelTableFunctionScan scan ) { + return visitChildren( scan ); + } + + + @Override + public AlgNode visit( LogicalRelValues values ) { + ImmutableList> 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.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 ); + } + + + @Override + public AlgNode visit( LogicalRelFilter filter ) { + return visitChild( filter, 0, filter.getInput() ); + } + + + @Override + public AlgNode visit(LogicalRelProject project) { + // Detect underpopulated columns if the user specifies fewer values than there are columns in the table + Optional indexOpt = findIdentifierIndex(project); + if (indexOpt.isEmpty()) { + return project; + } + + int index = indexOpt.get(); + AlgNode input = project.getInput(); + if (!(input instanceof LogicalRelValues inputValues)) { + return project; + } + + if ( hasLessInputsThanColumns(project, inputValues)) { + return createNewProjectWithIdentifiers(project, inputValues, index); + } + + return visitChild(project, 0, project.getInput()); + } + + private Optional findIdentifierIndex(LogicalRelProject project) { + return project.getRowType().getFields().stream() + .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> immutableValues = adjustInputValues(inputValues, index); + LogicalRelValues newInput = new LogicalRelValues( + inputValues.getCluster(), + inputValues.getTraitSet(), + inputValues.getRowType(), + immutableValues + ); + + List newProjects = createNewProjects(project, inputValues); + return project.copy(project.getTraitSet(), newInput, newProjects, project.getRowType()); + } + + private ImmutableList> adjustInputValues(LogicalRelValues inputValues, int index) { + return inputValues.tuples.stream() + .map(row -> { + PolyValue identifier = IdentifierUtils.getIdentifier(); + ImmutableList.Builder 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()); + } + + private List createNewProjects(LogicalRelProject project, LogicalRelValues inputValues) { + List 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() + )); + } + } + + return newProjects; + } + + + @Override + public AlgNode visit( LogicalRelJoin join ) { + return visitChildren( join ); + } + + + @Override + public AlgNode visit( LogicalRelCorrelate correlate ) { + return visitChildren( correlate ); + } + + + @Override + public AlgNode visit( LogicalRelUnion union ) { + return visitChildren( union ); + } + + + @Override + public AlgNode visit( LogicalRelIntersect intersect ) { + return visitChildren( intersect ); + } + + + @Override + public AlgNode visit( LogicalRelMinus minus ) { + return visitChildren( minus ); + } + + + @Override + public AlgNode visit( LogicalRelSort sort ) { + return visitChildren( sort ); + } + + + @Override + public AlgNode visit( LogicalRelExchange exchange ) { + return visitChildren( exchange ); + } + + + @Override + public AlgNode visit( LogicalConditionalExecute lce ) { + return visitChildren( 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; + } + + + @Override + public AlgNode visit( LogicalConstraintEnforcer enforcer ) { + return visitChildren( enforcer ); + } + + + @Override + public AlgNode visit( LogicalLpgModify modify ) { + return visitChildren( modify ); + } + + + @Override + public AlgNode visit( LogicalLpgScan scan ) { + return scan; + } + + + @Override + public AlgNode visit( LogicalLpgValues values ) { + return values; + } + + + @Override + public AlgNode visit( LogicalLpgFilter filter ) { + return visitChildren( filter ); + } + + + @Override + public AlgNode visit( LogicalLpgMatch match ) { + return visitChildren( match ); + } + + + @Override + public AlgNode visit( LogicalLpgProject project ) { + return visitChildren( project ); + } + + + @Override + public AlgNode visit( LogicalLpgAggregate aggregate ) { + return visitChildren( aggregate ); + } + + + @Override + public AlgNode visit( LogicalLpgSort sort ) { + return visitChildren( sort ); + } + + + @Override + public AlgNode visit( LogicalLpgUnwind unwind ) { + return visitChildren( unwind ); + } + + + @Override + public AlgNode visit( LogicalLpgTransformer transformer ) { + return visitChildren( transformer ); + } + + + @Override + public AlgNode visit( LogicalDocumentModify modify ) { + return visitChildren( modify ); + } + + + @Override + public AlgNode visit( LogicalDocumentAggregate aggregate ) { + return visitChildren( aggregate ); + } + + + @Override + public AlgNode visit( LogicalDocumentFilter filter ) { + return visitChildren( filter ); + } + + + @Override + public AlgNode visit( LogicalDocumentProject project ) { + return visitChildren( project ); + } + + + @Override + public AlgNode visit( LogicalDocumentScan scan ) { + return visitChildren( scan ); + } + + + @Override + public AlgNode visit( LogicalDocumentSort sort ) { + return visitChildren( sort ); + } + + + @Override + public AlgNode visit( LogicalDocumentTransformer transformer ) { + return visitChildren( transformer ); + } + + + @Override + public AlgNode visit( LogicalDocumentValues values ) { + return visitChildren( values ); + } + + + @Override + public AlgNode visit( AlgNode other ) { + return visitChildren( other ); + + } + +} diff --git a/core/src/main/java/org/polypheny/db/transaction/locking/IdentifierRegistry.java b/core/src/main/java/org/polypheny/db/transaction/locking/IdentifierRegistry.java index a7d8bcd10b..f1deba35c5 100644 --- a/core/src/main/java/org/polypheny/db/transaction/locking/IdentifierRegistry.java +++ b/core/src/main/java/org/polypheny/db/transaction/locking/IdentifierRegistry.java @@ -13,7 +13,7 @@ public class IdentifierRegistry { IdentifierRegistry( long maxIdentifierValue ) { this.availableIdentifiers = new TreeSet<>(); - this.availableIdentifiers.add( new IdentifierInterval( 0, maxIdentifierValue ) ); + this.availableIdentifiers.add( new IdentifierInterval( IdentifierUtils.MISSING_IDENTIFIER + 1, maxIdentifierValue ) ); } diff --git a/core/src/main/java/org/polypheny/db/transaction/locking/IdentifierUtils.java b/core/src/main/java/org/polypheny/db/transaction/locking/IdentifierUtils.java index 144b2ce555..d334f97381 100644 --- a/core/src/main/java/org/polypheny/db/transaction/locking/IdentifierUtils.java +++ b/core/src/main/java/org/polypheny/db/transaction/locking/IdentifierUtils.java @@ -16,20 +16,14 @@ package org.polypheny.db.transaction.locking; -import com.google.common.collect.ImmutableList; import java.text.MessageFormat; -import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; -import org.polypheny.db.algebra.AlgNode; -import org.polypheny.db.algebra.logical.relational.LogicalRelValues; import org.polypheny.db.catalog.logistic.Collation; import org.polypheny.db.ddl.DdlManager.ColumnTypeInformation; import org.polypheny.db.ddl.DdlManager.FieldInformation; -import org.polypheny.db.rex.RexLiteral; import org.polypheny.db.type.PolyType; -import org.polypheny.db.type.entity.numerical.PolyBigDecimal; import org.polypheny.db.type.entity.numerical.PolyLong; public class IdentifierUtils { @@ -55,62 +49,46 @@ public class IdentifierUtils { 1 ); + public static PolyLong getIdentifier() { return new PolyLong( IdentifierRegistry.INSTANCE.getEntryIdentifier() ); } + public static void throwIllegalFieldName() { throw new IllegalArgumentException( MessageFormat.format( "The field {0} is reserved for internal use and cannot be used.", - IdentifierUtils.IDENTIFIER_KEY) + IdentifierUtils.IDENTIFIER_KEY ) ); } - public static AlgNode overwriteIdentifierInInput( LogicalRelValues value ) { - List> newValues = new ArrayList<>(); - value.tuples.forEach(row -> { - List newRow = new ArrayList<>(row); - RexLiteral identifierLiteral = newRow.get(0); - newRow.set(0, IdentifierUtils.copyAndUpdateIdentifier(identifierLiteral, getIdentifier())); - newValues.add(newRow); - }); - - ImmutableList> immutableValues = new ImmutableList.Builder>() - .addAll(newValues.stream() - .map(ImmutableList::copyOf) - .toList()) - .build(); - - return new LogicalRelValues( - value.getCluster(), - value.getTraitSet(), - value.getRowType(), - immutableValues - ); - } - - private static RexLiteral copyAndUpdateIdentifier( RexLiteral identifierLiteral, PolyLong identifier ) { - return new RexLiteral(identifier, identifierLiteral.getType(), PolyType.DECIMAL); - } - public static List addIdentifierFieldIfAbsent( List fields ) { - if (fields.get(0).name().equals( IDENTIFIER_KEY )){ + if ( fields.get( 0 ).name().equals( IDENTIFIER_KEY ) ) { return fields; } List newFields = fields.stream() - .map( f -> new FieldInformation(f.name(), f.typeInformation(), f.collation(), f.defaultValue(), f.position() + 1) ) + .map( f -> new FieldInformation( f.name(), f.typeInformation(), f.collation(), f.defaultValue(), f.position() + 1 ) ) .collect( Collectors.toCollection( LinkedList::new ) ); - newFields.add(0, IDENTIFIER_FIELD_INFORMATION ); + newFields.add( 0, IDENTIFIER_FIELD_INFORMATION ); return newFields; } - public static void throwIfContainsIdentifierField(List fields) { - if (fields.stream().noneMatch( f -> f.name().equals(IDENTIFIER_FIELD_INFORMATION.name()))) { + + public static void throwIfContainsIdentifierField( List fields ) { + if ( fields.stream().noneMatch( f -> f.name().equals( IDENTIFIER_FIELD_INFORMATION.name() ) ) ) { return; } throwIllegalFieldName(); } + + public static void throwIfIsIdentifierKey( String string ) { + if ( !string.equals( IDENTIFIER_KEY ) ) { + return; + } + throwIllegalFieldName(); + } + } diff --git a/core/src/test/java/org/polypheny/db/transaction/locking/IdentifierRegistryTest.java b/core/src/test/java/org/polypheny/db/transaction/locking/IdentifierRegistryTest.java index 2a1ee63354..c03cc76d65 100644 --- a/core/src/test/java/org/polypheny/db/transaction/locking/IdentifierRegistryTest.java +++ b/core/src/test/java/org/polypheny/db/transaction/locking/IdentifierRegistryTest.java @@ -40,14 +40,14 @@ void testGetEntryIdentifierSequential() { long firstId = registry.getEntryIdentifier(); long secondId = registry.getEntryIdentifier(); - assertEquals( 0, firstId ); - assertEquals( 1, secondId ); + assertEquals( 1, firstId ); + assertEquals( 2, secondId ); } @Test void testGetEntryIdentifierUntilOverflow() { - for ( int i = 0; i < 100; i++ ) { + for ( int i = 0; i < 99; i++ ) { registry.getEntryIdentifier(); } Exception exception = assertThrows( IllegalStateException.class, registry::getEntryIdentifier ); @@ -61,7 +61,7 @@ void testReleaseSingleIdentifierBeginning() { registry.getEntryIdentifier(); registry.releaseEntryIdentifiers( Set.of( firstIdentifier ) ); - assertEquals( 0, registry.getEntryIdentifier() ); + assertEquals( 1, registry.getEntryIdentifier() ); } @@ -75,7 +75,7 @@ void testReleaseSingleIdentifierMiddle() { registry.getEntryIdentifier(); } registry.releaseEntryIdentifiers( Set.of( middleIdentifier ) ); - assertEquals( 25, registry.getEntryIdentifier() ); + assertEquals( 26, registry.getEntryIdentifier() ); } @@ -92,7 +92,7 @@ void testReleaseMultipleIdentifiersConsequtive() { registry.getEntryIdentifier(); } registry.releaseEntryIdentifiers( identifiers ); - for ( int i = 20; i < 40; i++ ) { + for ( int i = 21; i < 40; i++ ) { assertEquals( i, registry.getEntryIdentifier() ); } } @@ -104,17 +104,19 @@ void testReleaseMultipleIdentifiersInterleaved() { Set oddIdentifiers = new HashSet<>(); for ( int i = 0; i < 60; i++ ) { - if ( i % 2 == 0 ) { - evenIdentifiers.add( registry.getEntryIdentifier() ); + long id = registry.getEntryIdentifier(); + if ( id % 2 == 0 ) { + evenIdentifiers.add( id ); continue; } - oddIdentifiers.add( registry.getEntryIdentifier() ); + oddIdentifiers.add( id ); } registry.releaseEntryIdentifiers( evenIdentifiers ); for ( int i = 0; i < 30; i++ ) { - assertEquals( 0, registry.getEntryIdentifier() % 2 ); + long id = registry.getEntryIdentifier(); + assertEquals( 0, id % 2); } } diff --git a/dbms/src/main/java/org/polypheny/db/ddl/DdlManagerImpl.java b/dbms/src/main/java/org/polypheny/db/ddl/DdlManagerImpl.java index 124584581a..9cb37d1a35 100644 --- a/dbms/src/main/java/org/polypheny/db/ddl/DdlManagerImpl.java +++ b/dbms/src/main/java/org/polypheny/db/ddl/DdlManagerImpl.java @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableList; +import io.grpc.xds.shaded.com.google.api.expr.v1alpha1.Expr.Ident; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; @@ -458,6 +459,7 @@ private void updateColumnPosition( LogicalTable table, LogicalColumn column, int @Override public void createColumn( String columnName, LogicalTable table, String beforeColumnName, String afterColumnName, ColumnTypeInformation type, boolean nullable, PolyValue defaultValue, Statement statement ) { columnName = adjustNameIfNeeded( columnName, table.namespaceId ); + IdentifierUtils.throwIfIsIdentifierKey( columnName ); // Check if the column either allows null values or has a default value defined. if ( defaultValue == null && !nullable ) { throw new GenericRuntimeException( "Column is not nullable and does not have a default value defined." ); @@ -850,6 +852,7 @@ public void createUniqueConstraint( LogicalTable table, List columnNames @Override public void dropColumn( LogicalTable table, String columnName, Statement statement ) { + IdentifierUtils.throwIfIsIdentifierKey( columnName ); List columns = catalog.getSnapshot().rel().getColumns( table.id ); if ( columns.size() < 2 ) { throw new GenericRuntimeException( "Cannot drop sole column of table %s", table.name ); @@ -1097,6 +1100,7 @@ public void dropPrimaryKey( LogicalTable table ) { @Override public void setColumnType( LogicalTable table, String columnName, ColumnTypeInformation type, Statement statement ) { + IdentifierUtils.throwIfIsIdentifierKey( columnName ); // Make sure that this is a table of type TABLE (and not SOURCE) checkIfDdlPossible( table.entityType ); @@ -1616,6 +1620,9 @@ public void renameCollection( LogicalCollection collection, String newName, Stat @Override public void renameColumn( LogicalTable table, String columnName, String newColumnName, Statement statement ) { + IdentifierUtils.throwIfIsIdentifierKey( columnName ); + IdentifierUtils.throwIfIsIdentifierKey( newColumnName ); + LogicalColumn logicalColumn = catalog.getSnapshot().rel().getColumn( table.id, columnName ).orElseThrow(); if ( catalog.getSnapshot().rel().getColumn( table.id, newColumnName ).isPresent() ) { @@ -2059,7 +2066,7 @@ public void createTable( long namespaceId, String name, List f Map ids = new HashMap<>(); IdentifierUtils.throwIfContainsIdentifierField( fields ); - fields = IdentifierUtils.addIdentifierFieldIfAbsent(fields); + fields = IdentifierUtils.addIdentifierFieldIfAbsent( fields ); for ( FieldInformation information : fields ) { ids.put( information.name(), addColumn( namespaceId, information.name(), information.typeInformation(), information.collation(), information.defaultValue(), logical.id, information.position() + 1 ) ); // pos + 1 to make space for entry identifier column @@ -2105,6 +2112,7 @@ public void createTable( long namespaceId, String name, List f catalog.updateSnapshot(); } + @NotNull private Pair createSinglePartition( long namespaceId, LogicalTable logical ) { AllocationPartitionGroup group = catalog.getAllocRel( namespaceId ).addPartitionGroup( logical.id, UNPARTITIONED, namespaceId, PartitionType.NONE, 1, false ); diff --git a/dbms/src/test/java/org/polypheny/db/transaction/EntityIdentifierTests.java b/dbms/src/test/java/org/polypheny/db/transaction/EntityIdentifierTests.java index 3701c90059..4d20e6d646 100644 --- a/dbms/src/test/java/org/polypheny/db/transaction/EntityIdentifierTests.java +++ b/dbms/src/test/java/org/polypheny/db/transaction/EntityIdentifierTests.java @@ -20,8 +20,10 @@ import java.sql.Connection; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.List; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.polypheny.db.TestHelper; @@ -48,7 +50,26 @@ public void testCreateTable() throws SQLException { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { - statement.executeUpdate( "CREATE TABLE identifiers (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); + statement.executeUpdate( "CREATE TABLE identifiers (a VARCHAR(8) NOT NULL, b VARCHAR(8), PRIMARY KEY (a))" ); + connection.commit(); + } finally { + statement.executeUpdate( "DROP TABLE IF EXISTS identifiers" ); + connection.commit(); + } + } + } + } + + @Test + public void testCreateTableIllegalColumnName() throws SQLException { + try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { + Connection connection = jdbcConnection.getConnection(); + try ( Statement statement = connection.createStatement() ) { + try { + assertThrows( + PrismInterfaceServiceException.class, + () -> statement.executeUpdate( "CREATE TABLE identifiers (a INTEGER NOT NULL, _eid INTEGER, PRIMARY KEY (a))" ) + ); connection.commit(); } finally { statement.executeUpdate( "DROP TABLE IF EXISTS identifiers" ); @@ -65,8 +86,8 @@ public void testInsertUnparameterizedWithColumnNames() throws SQLException { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { - statement.executeUpdate( "CREATE TABLE identifiers (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); - statement.executeUpdate( "INSERT INTO identifiers (a, b) VALUES (1, 2)" ); + statement.executeUpdate( "CREATE TABLE identifiers (a VARCHAR(8) NOT NULL, b VARCHAR(8), PRIMARY KEY (a))" ); + statement.executeUpdate( "INSERT INTO identifiers (a, b) VALUES ('first', 'second')" ); connection.commit(); } finally { @@ -84,10 +105,10 @@ public void testInsertPreparedWithColumnNames() throws SQLException { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { - statement.executeUpdate( "CREATE TABLE identifiers (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); + statement.executeUpdate( "CREATE TABLE identifiers (a VARCHAR(8) NOT NULL, b VARCHAR(8), PRIMARY KEY (a))" ); PreparedStatement preparedStatement = connection.prepareStatement( "INSERT INTO identifiers (a, b) VALUES (?, ?)" ); - preparedStatement.setInt( 1, 1 ); - preparedStatement.setInt( 2, 2 ); + preparedStatement.setString( 1, "first" ); + preparedStatement.setString( 2, "second" ); preparedStatement.executeUpdate(); connection.commit(); } finally { @@ -105,8 +126,8 @@ public void testInsertMultipleUnparameterizedWithColumnNames() throws SQLExcepti Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { - statement.executeUpdate( "CREATE TABLE identifiers (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); - statement.executeUpdate( "INSERT INTO identifiers (a, b) VALUES (1, 2), (3, 4)" ); + statement.executeUpdate( "CREATE TABLE identifiers (a VARCHAR(8) NOT NULL, b VARCHAR(8), PRIMARY KEY (a))" ); + statement.executeUpdate( "INSERT INTO identifiers (a, b) VALUES ('first', 'second'), ('third', 'fourth')" ); connection.commit(); } finally { @@ -124,13 +145,13 @@ public void testInsertMultiplePreparedWithColumnNames() throws SQLException { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { - statement.executeUpdate( "CREATE TABLE identifiers (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); + statement.executeUpdate( "CREATE TABLE identifiers (a VARCHAR(8) NOT NULL, b VARCHAR(8), PRIMARY KEY (a))" ); PreparedStatement preparedStatement = connection.prepareStatement( "INSERT INTO identifiers (a, b) VALUES (?, ?)" ); - preparedStatement.setInt( 1, 1 ); - preparedStatement.setInt( 2, 2 ); + preparedStatement.setString( 1, "first" ); + preparedStatement.setString( 2, "second" ); preparedStatement.addBatch(); - preparedStatement.setInt( 1, 3 ); - preparedStatement.setInt( 2, 4 ); + preparedStatement.setString( 1, "third" ); + preparedStatement.setString( 2, "fourth" ); preparedStatement.executeBatch(); connection.commit(); } finally { @@ -142,20 +163,32 @@ public void testInsertMultiplePreparedWithColumnNames() throws SQLException { } - // This currently fails due to a mismatch between the column names in the table which are mistakenly compared somehow + //TODO: fix this @Test public void testInsertFromTableWithDifferentColumnNames() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { - statement.executeUpdate( "CREATE TABLE identifiers1 (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); - statement.executeUpdate( "INSERT INTO identifiers1 (a, b) VALUES (1, 2), (3, 4)" ); + statement.executeUpdate( "CREATE TABLE identifiers1 (a VARCHAR(8) NOT NULL, b VARCHAR(8), PRIMARY KEY (a))" ); + statement.executeUpdate( "INSERT INTO identifiers1 (a, b) VALUES ('first', 'second'), ('third', 'fourth')" ); - statement.executeUpdate( "CREATE TABLE identifiers2 (x INTEGER NOT NULL, y INTEGER, PRIMARY KEY (x))" ); + statement.executeUpdate( "CREATE TABLE identifiers2 (x VARCHAR(8) NOT NULL, y VARCHAR(8), PRIMARY KEY (x))" ); statement.executeUpdate( "INSERT INTO identifiers2 (x, y) SELECT a, b FROM identifiers1" ); - //TODO TH: check that new identifiers had been assigned instead of copying from the first table + // check that new identifiers had been assigned instead of copying from the first table + try ( ResultSet rs = statement.executeQuery( """ + SELECT 1 + FROM identifiers1 id1 + WHERE EXISTS ( + SELECT 1 + FROM identifiers2 id2 + WHERE id1._eid = id2._eid + ); + """ ) ) { + TestHelper.checkResultSet( rs, List.of() ); + } + connection.commit(); } finally { statement.executeUpdate( "DROP TABLE IF EXISTS identifiers1" ); @@ -166,17 +199,18 @@ public void testInsertFromTableWithDifferentColumnNames() throws SQLException { } } + @Test public void testInsertFromTableWithoutTargetColumnNames() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { - statement.executeUpdate( "CREATE TABLE identifiers1 (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); - statement.executeUpdate( "INSERT INTO identifiers1 (a, b) VALUES (1, 2), (3, 4)" ); + statement.executeUpdate( "CREATE TABLE identifiers1 (a VARCHAR(8) NOT NULL, b VARCHAR(8), PRIMARY KEY (a))" ); + statement.executeUpdate( "INSERT INTO identifiers1 (a, b) VALUES ('first', 'second'), ('third', 'fourth')" ); - statement.executeUpdate( "CREATE TABLE identifiers2 (x INTEGER NOT NULL, y INTEGER, PRIMARY KEY (x))" ); - statement.executeUpdate( "INSERT INTO identifiers1 SELECT a, b FROM identifiers2" ); + statement.executeUpdate( "CREATE TABLE identifiers2 (x VARCHAR(8) NOT NULL, y VARCHAR(8), PRIMARY KEY (x))" ); + statement.executeUpdate( "INSERT INTO identifiers2 SELECT a, b FROM identifiers1" ); //TODO TH: check that new identifiers had been assigned instead of copying from the first table connection.commit(); @@ -196,11 +230,11 @@ public void testInsertFromTableWithoutColumnNames() throws SQLException { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { - statement.executeUpdate( "CREATE TABLE identifiers1 (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); - statement.executeUpdate( "INSERT INTO identifiers1 (a, b) VALUES (1, 2), (3, 4)" ); + statement.executeUpdate( "CREATE TABLE identifiers1 (a VARCHAR(8) NOT NULL, b VARCHAR(8), PRIMARY KEY (a))" ); + statement.executeUpdate( "INSERT INTO identifiers1 (a, b) VALUES ('first', 'second'), ('third', 'fourth')" ); - statement.executeUpdate( "CREATE TABLE identifiers2 (x INTEGER NOT NULL, y INTEGER, PRIMARY KEY (x))" ); - statement.executeUpdate( "INSERT INTO identifiers1 SELECT * FROM identifiers2" ); + statement.executeUpdate( "CREATE TABLE identifiers2 (x VARCHAR(8) NOT NULL, y VARCHAR(8), PRIMARY KEY (x))" ); + statement.executeUpdate( "INSERT INTO identifiers2 SELECT * FROM identifiers1" ); //TODO TH: check that new identifiers had been assigned instead of copying from the first table connection.commit(); @@ -214,32 +248,14 @@ public void testInsertFromTableWithoutColumnNames() throws SQLException { } - @Test - public void testInsertUnparameterizedDefaultExplicit() throws SQLException { - try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { - Connection connection = jdbcConnection.getConnection(); - try ( Statement statement = connection.createStatement() ) { - try { - statement.executeUpdate( "CREATE TABLE identifiers1 (a INTEGER NOT NULL, b INTEGER DEFAULT 42, PRIMARY KEY (a))" ); - statement.executeUpdate( "INSERT INTO identifiers1 (a, b) VALUES (1, DEFAULT)" ); - connection.commit(); - } finally { - statement.executeUpdate( "DROP TABLE IF EXISTS identifiers1" ); - connection.commit(); - } - } - } - } - - @Test public void testInsertUnparameterizedDefaultOmitted() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { - statement.executeUpdate( "CREATE TABLE identifiers1 (a INTEGER NOT NULL, b INTEGER DEFAULT 42, PRIMARY KEY (a))" ); - statement.executeUpdate( "INSERT INTO identifiers1 (a) VALUES (1)" ); + statement.executeUpdate( "CREATE TABLE identifiers1 (a VARCHAR(8) NOT NULL, b VARCHAR(8) DEFAULT 'foo', PRIMARY KEY (a))" ); + statement.executeUpdate( "INSERT INTO identifiers1 (a) VALUES ('first')" ); connection.commit(); } finally { statement.executeUpdate( "DROP TABLE IF EXISTS identifiers1" ); @@ -249,6 +265,10 @@ public void testInsertUnparameterizedDefaultOmitted() throws SQLException { } } + // ------------------------------------------------------------------------ + // DEFAULT VALUES STUFF + // ------------------------------------------------------------------------ + @Test public void testInsertUnparameterizedDefaultExplicitNoColumnNames() throws SQLException { @@ -256,8 +276,8 @@ public void testInsertUnparameterizedDefaultExplicitNoColumnNames() throws SQLEx Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { - statement.executeUpdate( "CREATE TABLE identifiers1 (a INTEGER NOT NULL, b INTEGER DEFAULT 42, PRIMARY KEY (a))" ); - statement.executeUpdate( "INSERT INTO identifiers1 VALUES (1, DEFAULT)" ); + statement.executeUpdate( "CREATE TABLE identifiers1 (a VARCHAR(8) NOT NULL, b VARCHAR(8) DEFAULT 'foo', PRIMARY KEY (a))" ); + statement.executeUpdate( "INSERT INTO identifiers1 VALUES ('first', 'second')" ); connection.commit(); } finally { statement.executeUpdate( "DROP TABLE IF EXISTS identifiers1" ); @@ -269,16 +289,17 @@ public void testInsertUnparameterizedDefaultExplicitNoColumnNames() throws SQLEx @Test + // TODO TH: Does this work? public void testInsertParameterizedDefaultExplicit() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { - statement.executeUpdate( "CREATE TABLE identifiers1 (a INTEGER NOT NULL, b INTEGER DEFAULT 42, PRIMARY KEY (a))" ); + statement.executeUpdate( "CREATE TABLE identifiers1 (a VARCHAR(8) NOT NULL, b VARCHAR(8) DEFAULT 'foo', PRIMARY KEY (a))" ); - String insertSql = "INSERT INTO identifiers1 (a, b) VALUES (?, DEFAULT)"; + String insertSql = "INSERT INTO identifiers1 (a, b) VALUES (?)"; try ( PreparedStatement preparedStatement = connection.prepareStatement( insertSql ) ) { - preparedStatement.setInt( 1, 1 ); + preparedStatement.setString( 1, "first" ); preparedStatement.executeUpdate(); } connection.commit(); @@ -292,38 +313,17 @@ public void testInsertParameterizedDefaultExplicit() throws SQLException { @Test + // TODO TH: Does this work? public void testInsertParameterizedDefaultOmitted() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { - statement.executeUpdate( "CREATE TABLE identifiers1 (a INTEGER NOT NULL, b INTEGER DEFAULT 42, PRIMARY KEY (a))" ); + statement.executeUpdate( "CREATE TABLE identifiers1 (a VARCHAR(8) NOT NULL, b VARCHAR(8) DEFAULT 'foo', PRIMARY KEY (a))" ); String insertSql = "INSERT INTO identifiers1 (a) VALUES (?)"; try ( PreparedStatement preparedStatement = connection.prepareStatement( insertSql ) ) { - preparedStatement.setInt( 1, 1 ); - preparedStatement.executeUpdate(); - } - connection.commit(); - } finally { - statement.executeUpdate( "DROP TABLE IF EXISTS identifiers1" ); - connection.commit(); - } - } - } - } - - - @Test - public void testInsertParameterizedDefaultExplicitNoColumnNames() throws SQLException { - try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { - Connection connection = jdbcConnection.getConnection(); - try ( Statement statement = connection.createStatement() ) { - try { - statement.executeUpdate( "CREATE TABLE identifiers1 (a INTEGER NOT NULL, b INTEGER DEFAULT 42, PRIMARY KEY (a))" ); - String insertSql = "INSERT INTO identifiers1 VALUES (?, DEFAULT)"; - try ( PreparedStatement preparedStatement = connection.prepareStatement( insertSql ) ) { - preparedStatement.setInt( 1, 1 ); // Set value for 'a' + preparedStatement.setString( 1, "first" ); preparedStatement.executeUpdate(); } connection.commit(); @@ -337,49 +337,14 @@ public void testInsertParameterizedDefaultExplicitNoColumnNames() throws SQLExce @Test - public void testInsertMultipleUnparameterizedDefaultExplicit() throws SQLException { - try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { - Connection connection = jdbcConnection.getConnection(); - try ( Statement statement = connection.createStatement() ) { - try { - statement.executeUpdate( "CREATE TABLE identifiers1 (a INTEGER NOT NULL, b INTEGER DEFAULT 42, PRIMARY KEY (a))" ); - statement.executeUpdate( "INSERT INTO identifiers1 (a, b) VALUES (1, DEFAULT), (2, 3), (3, DEFAULT)" ); - connection.commit(); - } finally { - statement.executeUpdate( "DROP TABLE IF EXISTS identifiers" ); - connection.commit(); - } - } - } - } - - - @Test - public void testInsertMultipleUnparameterizedDefaultExplicitNoColumnNames() throws SQLException { - try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { - Connection connection = jdbcConnection.getConnection(); - try ( Statement statement = connection.createStatement() ) { - try { - statement.executeUpdate( "CREATE TABLE identifiers1 (a INTEGER NOT NULL, b INTEGER DEFAULT 42, PRIMARY KEY (a))" ); - statement.executeUpdate( "INSERT INTO identifiers1 VALUES (1, DEFAULT), (2, 3), (3, DEFAULT)" ); - connection.commit(); - } finally { - statement.executeUpdate( "DROP TABLE IF EXISTS identifiers" ); - connection.commit(); - } - } - } - } - - - @Test + // TODO TH: Does this work? public void testInsertMultipleUnparameterizedDefaultOmitted() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { - statement.executeUpdate( "CREATE TABLE identifiers1 (a INTEGER NOT NULL, b INTEGER DEFAULT 42, PRIMARY KEY (a))" ); - statement.executeUpdate( "INSERT INTO identifiers1 (a) VALUES (1), (2), (3)" ); + statement.executeUpdate( "CREATE TABLE identifiers1 (a VARCHAR(8) NOT NULL, b VARCHAR(8) DEFAULT 'foo', PRIMARY KEY (a))" ); + statement.executeUpdate( "INSERT INTO identifiers1 (a) VALUES ('first'), ('second'), ('third')" ); connection.commit(); } finally { statement.executeUpdate( "DROP TABLE IF EXISTS identifiers" ); @@ -391,72 +356,18 @@ public void testInsertMultipleUnparameterizedDefaultOmitted() throws SQLExceptio @Test - public void testInsertMultipleParameterizedDefaultExplicit() throws SQLException { - try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { - Connection connection = jdbcConnection.getConnection(); - try ( Statement statement = connection.createStatement() ) { - try { - statement.executeUpdate( "CREATE TABLE identifiers1 (a INTEGER NOT NULL, b INTEGER DEFAULT 42, PRIMARY KEY (a))" ); - - String insertSql = "INSERT INTO identifiers1 (a, b) VALUES (?, DEFAULT), (?, ?), (?, DEFAULT)"; - try ( PreparedStatement preparedStatement = connection.prepareStatement( insertSql ) ) { - preparedStatement.setInt( 1, 1 ); - preparedStatement.setInt( 2, 2 ); - preparedStatement.setInt( 3, 3 ); - preparedStatement.setInt( 4, 3 ); - preparedStatement.executeUpdate(); - } - - connection.commit(); - } finally { - statement.executeUpdate( "DROP TABLE IF EXISTS identifiers1" ); - connection.commit(); - } - } - } - } - - - @Test - public void testInsertMultipleParameterizedDefaultExplicitNoColumnNames() throws SQLException { - try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { - Connection connection = jdbcConnection.getConnection(); - try ( Statement statement = connection.createStatement() ) { - try { - statement.executeUpdate( "CREATE TABLE identifiers1 (a INTEGER NOT NULL, b INTEGER DEFAULT 42, PRIMARY KEY (a))" ); - - String insertSql = "INSERT INTO identifiers1 VALUES (?, DEFAULT), (?, ?), (?, DEFAULT)"; - try ( PreparedStatement preparedStatement = connection.prepareStatement( insertSql ) ) { - preparedStatement.setInt( 1, 1 ); - preparedStatement.setInt( 2, 2 ); - preparedStatement.setInt( 3, 3 ); - preparedStatement.setInt( 4, 3 ); - preparedStatement.executeUpdate(); - } - - connection.commit(); - } finally { - statement.executeUpdate( "DROP TABLE IF EXISTS identifiers1" ); - connection.commit(); - } - } - } - } - - - @Test + // TODO TH: Does this work? public void testInsertMultipleParameterizedDefaultOmitted() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { - statement.executeUpdate( "CREATE TABLE identifiers1 (a INTEGER NOT NULL, b INTEGER DEFAULT 42, PRIMARY KEY (a))" ); - + statement.executeUpdate( "CREATE TABLE identifiers1 (a VARCHAR(8) NOT NULL, b VARCHAR(8) DEFAULT 'foo', PRIMARY KEY (a))" ); String insertSql = "INSERT INTO identifiers1 (a) VALUES (?), (?), (?)"; try ( PreparedStatement preparedStatement = connection.prepareStatement( insertSql ) ) { - preparedStatement.setInt( 1, 1 ); - preparedStatement.setInt( 2, 2 ); - preparedStatement.setInt( 3, 3 ); + preparedStatement.setString( 1, "first" ); + preparedStatement.setString( 2, "second" ); + preparedStatement.setString( 3, "third" ); preparedStatement.executeUpdate(); } @@ -471,13 +382,14 @@ public void testInsertMultipleParameterizedDefaultOmitted() throws SQLException @Test + // TODO TH: Does this work? public void testInsertUnparameterizedNoColumnNames() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { - statement.executeUpdate( "CREATE TABLE identifiers (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); - statement.executeUpdate( "INSERT INTO identifiers VALUES (1, 2)" ); + statement.executeUpdate( "CREATE TABLE identifiers (a VARCHAR(8) NOT NULL, b VARCHAR(8), PRIMARY KEY (a))" ); + statement.executeUpdate( "INSERT INTO identifiers VALUES ('first', 'second')" ); connection.commit(); } finally { statement.executeUpdate( "DROP TABLE IF EXISTS identifiers" ); @@ -489,16 +401,17 @@ public void testInsertUnparameterizedNoColumnNames() throws SQLException { @Test + // TODO TH: Does this work? public void testInsertPreparedNoColumnNames() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { - statement.executeUpdate( "CREATE TABLE identifiers (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); + statement.executeUpdate( "CREATE TABLE identifiers (a VARCHAR(8) NOT NULL, b VARCHAR(8), PRIMARY KEY (a))" ); String insertSql = "INSERT INTO identifiers VALUES (?, ?)"; try ( PreparedStatement preparedStatement = connection.prepareStatement( insertSql ) ) { - preparedStatement.setInt( 1, 1 ); - preparedStatement.setInt( 2, 2 ); + preparedStatement.setString( 1, "first" ); + preparedStatement.setString( 2, "second" ); preparedStatement.executeUpdate(); } connection.commit(); @@ -532,7 +445,6 @@ public void testInsertUnparameterizedColumnNameConflictSameType() throws SQLExce @Test - //TODO TH: should fail public void testInsertUnparameterizedColumnNameConflictDifferentType() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); @@ -553,14 +465,16 @@ public void testInsertUnparameterizedColumnNameConflictDifferentType() throws SQ @Test - //TODO TH: should fail public void testInsertUnparameterizedIdentifierManipulationInsert() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { statement.executeUpdate( "CREATE TABLE identifiers (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); - statement.executeUpdate( "INSERT INTO identifiers VALUES (1, 2, 3)" ); + assertThrows( + PrismInterfaceServiceException.class, + () -> statement.executeUpdate( "INSERT INTO identifiers VALUES (-32, 2, 3)" ) + ); connection.commit(); } finally { statement.executeUpdate( "DROP TABLE IF EXISTS identifiers" ); @@ -572,15 +486,17 @@ public void testInsertUnparameterizedIdentifierManipulationInsert() throws SQLEx @Test - //TODO TH: should fail public void testUpdateUnparameterizedIdentifierManipulation() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { statement.executeUpdate( "CREATE TABLE identifiers (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); - statement.executeUpdate( "INSERT INTO identifiers VALUES (1, 2)" ); - statement.executeUpdate( "UPDATE identifiers SET _eid = 1 WHERE a = 1 AND b = 2" ); + statement.executeUpdate( "INSERT INTO identifiers (a, b) VALUES (1, 2)" ); + assertThrows( + PrismInterfaceServiceException.class, + () -> statement.executeUpdate( "UPDATE identifiers SET _eid = 32 WHERE a = 1 AND b = 2" ) + ); connection.commit(); } finally { statement.executeUpdate( "DROP TABLE IF EXISTS identifiers" ); @@ -592,16 +508,16 @@ public void testUpdateUnparameterizedIdentifierManipulation() throws SQLExceptio @Test - //TODO TH: should fail public void testDropIdentifierColumnUnparameterized() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { statement.executeUpdate( "CREATE TABLE identifiers (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); - statement.executeUpdate( "INSERT INTO identifiers VALUES (1, 2)" ); - statement.executeUpdate( "ALTER TABLE identifiers DROP COLUMN _eid" ); - + assertThrows( + PrismInterfaceServiceException.class, + () -> statement.executeUpdate( "ALTER TABLE identifiers DROP COLUMN _eid" ) + ); connection.commit(); } finally { statement.executeUpdate( "DROP TABLE IF EXISTS identifiers" ); @@ -613,36 +529,16 @@ public void testDropIdentifierColumnUnparameterized() throws SQLException { @Test - //TODO TH: should fail public void testAddIdentifierColumnUnparameterized() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { statement.executeUpdate( "CREATE TABLE identifiers (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); - statement.executeUpdate( "ALTER TABLE identifiers DROP COLUMN _eid" ); // this should already fail here - statement.executeUpdate( "ALTER TABLE identifiers ADD COLUMN _eid BIGINT" ); - - connection.commit(); - } finally { - statement.executeUpdate( "DROP TABLE IF EXISTS identifiers" ); - connection.commit(); - } - } - } - } - - - @Test - //TODO TH: should fail - public void testDropMulitpleColumnUnparameterized() throws SQLException { - try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { - Connection connection = jdbcConnection.getConnection(); - try ( Statement statement = connection.createStatement() ) { - try { - statement.executeUpdate( "CREATE TABLE identifiers (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); - statement.executeUpdate( "INSERT INTO identifiers VALUES (1, 2)" ); - statement.executeUpdate( "ALTER TABLE identifiers DROP COLUMN _eid DROP COLUMN b" ); + assertThrows( + PrismInterfaceServiceException.class, + () -> statement.executeUpdate( "ALTER TABLE identifiers ADD COLUMN _eid BIGINT" ) + ); connection.commit(); } finally { @@ -655,15 +551,16 @@ public void testDropMulitpleColumnUnparameterized() throws SQLException { @Test - //TODO TH: should fail - public void testAddMulitpleColumnUnparameterized() throws SQLException { + public void testRenameIdentifierColumnUnparameterized() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { statement.executeUpdate( "CREATE TABLE identifiers (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); - statement.executeUpdate( "ALTER TABLE identifiers ADD COLUMN _eid BIGINT ADD COLUMN c INTEGER" ); - + assertThrows( + PrismInterfaceServiceException.class, + () -> statement.executeUpdate( "ALTER TABLE identifiers RENAME COLUMN _eid TO nowItsBroken" ) + ); connection.commit(); } finally { statement.executeUpdate( "DROP TABLE IF EXISTS identifiers" ); @@ -675,15 +572,16 @@ public void testAddMulitpleColumnUnparameterized() throws SQLException { @Test - //TODO TH: should fail - public void testRenameIdentifierColumnUnparameterized() throws SQLException { + public void testRenameNonIdentifierColumnUnparameterized() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { statement.executeUpdate( "CREATE TABLE identifiers (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); - statement.executeUpdate( "ALTER TABLE identifiers RENAME COLUMN _eid TO nowItsBroken" ); - + assertThrows( + PrismInterfaceServiceException.class, + () -> statement.executeUpdate( "ALTER TABLE identifiers RENAME COLUMN b TO _eid" ) + ); connection.commit(); } finally { statement.executeUpdate( "DROP TABLE IF EXISTS identifiers" ); @@ -695,15 +593,16 @@ public void testRenameIdentifierColumnUnparameterized() throws SQLException { @Test - //TODO TH: should fail public void testChangeDataTypeOfIdentifierColumnUnparameterized() throws SQLException { try ( JdbcConnection jdbcConnection = new JdbcConnection( true ) ) { Connection connection = jdbcConnection.getConnection(); try ( Statement statement = connection.createStatement() ) { try { statement.executeUpdate( "CREATE TABLE identifiers (a INTEGER NOT NULL, b INTEGER, PRIMARY KEY (a))" ); - statement.executeUpdate( "ALTER TABLE identifiers MODIFY COLUMN _eid SET TYPE VARCHAR(15)" ); - + assertThrows( + PrismInterfaceServiceException.class, + () -> statement.executeUpdate( "ALTER TABLE identifiers MODIFY COLUMN _eid SET TYPE VARCHAR(15)" ) + ); connection.commit(); } finally { statement.executeUpdate( "DROP TABLE IF EXISTS identifiers" );