vaticle / typedb

TypeDB: the polymorphic database powered by types
https://typedb.com
Mozilla Public License 2.0
3.72k stars 338 forks source link

Using `RelationType.set_relates` can corrupt the schema #6985

Closed james-whiteside closed 4 months ago

james-whiteside commented 6 months ago

Description

When using RelationType.set_relates(transaction, "role", "superrole") where the relation type already has a role with name role, the method executes succesfully and the transaction is committed, but then any future attempts to read the role (tested with Database.schema and with the Studio type browser) throw a null pointer exception.

Environment

  1. TypeDB distribution: Core
  2. TypeDB version: 2.25.7
  3. Environment: MacOS
  4. Driver version: Python 2.26.1

Reproducible Steps

Run following code:

database = "social-media"

with TypeDB.core_driver("localhost:1729") as driver:
    driver.databases.create(database)

    with driver.session(database, SessionType.SCHEMA) as session:
        with session.transaction(TransactionType.WRITE) as transaction:
            person = transaction.concepts.put_entity_type("person").resolve()
            marriage = transaction.concepts.put_relation_type("marriage").resolve()
            marriage.set_relates(transaction, "spouse").resolve()
            spouse = marriage.get_relates(transaction, "spouse").resolve()
            person.set_plays(transaction, spouse).resolve()
            dating = transaction.concepts.put_relation_type("dating").resolve()
            dating.set_relates(transaction, "partner").resolve()
            partner = dating.get_relates(transaction, "partner").resolve()
            person.set_plays(transaction, partner).resolve()

            transaction.commit()

        print(driver.databases.get(database).schema())

        with session.transaction(TransactionType.WRITE) as transaction:
            relationship = transaction.concepts.put_relation_type("relationship").resolve()
            relationship.set_relates(transaction, "related").resolve()
            marriage = transaction.concepts.get_relation_type("marriage").resolve()
            marriage.set_supertype(transaction, relationship).resolve()
            marriage.set_relates(transaction, "spouse", "related").resolve()
            dating = transaction.concepts.get_relation_type("dating").resolve()
            dating.set_supertype(transaction, relationship).resolve()
            dating.set_relates(transaction, "partner", "related").resolve()

            transaction.commit()

with TypeDB.core_driver("localhost:1729") as driver:
    print(driver.databases.get(database).schema())

Expected result

Either:

The first outcome is preferable, because otherwise there is currently no way to reassign the supertype of an existing role.

Additional information

Relevant logs from TypeDB or Driver:

Dec 20, 2023 12:24:26 PM io.grpc.internal.SerializingExecutor run
SEVERE: Exception while executing runnable io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$1HalfClosed@2904758a
java.lang.NullPointerException
    at com.vaticle.typedb.core.graph.vertex.impl.TypeVertexImpl$Persisted.<init>(TypeVertexImpl.java:409)
    at com.vaticle.typedb.core.graph.TypeGraph.lambda$convert$51(TypeGraph.java:439)
    at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1705)
    at com.vaticle.typedb.core.graph.TypeGraph.convert(TypeGraph.java:438)
    at com.vaticle.typedb.core.graph.edge.impl.TypeEdgeImpl$Persisted.overridden(TypeEdgeImpl.java:489)
    at com.vaticle.typedb.core.concept.type.impl.RelationTypeImpl.getRelatesOverridden(RelationTypeImpl.java:213)
    at com.vaticle.typedb.core.concept.type.impl.RelationTypeImpl.lambda$writeRelates$19(RelationTypeImpl.java:307)
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
    at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:395)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
    at com.vaticle.typedb.core.concept.type.impl.RelationTypeImpl.writeRelates(RelationTypeImpl.java:303)
    at com.vaticle.typedb.core.concept.type.impl.RelationTypeImpl.getSyntax(RelationTypeImpl.java:297)
    at com.vaticle.typedb.core.concept.type.impl.ThingTypeImpl.getSyntaxRecursive(ThingTypeImpl.java:159)
    at com.vaticle.typedb.core.concept.type.impl.ThingTypeImpl.lambda$getSyntaxRecursive$1(ThingTypeImpl.java:162)
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
    at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:395)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
    at com.vaticle.typedb.core.concept.type.impl.ThingTypeImpl.getSyntaxRecursive(ThingTypeImpl.java:162)
    at com.vaticle.typedb.core.concept.ConceptManager.lambda$typesSyntax$18(ConceptManager.java:396)
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
    at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:395)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
    at com.vaticle.typedb.core.concept.ConceptManager.typesSyntax(ConceptManager.java:396)
    at com.vaticle.typedb.core.database.CoreDatabase.schema(CoreDatabase.java:443)
    at com.vaticle.typedb.core.server.TypeDBService.databaseSchema(TypeDBService.java:263)
    at com.vaticle.typedb.protocol.TypeDBGrpc$MethodHandlers.invoke(TypeDBGrpc.java:1553)
    at io.grpc.stub.ServerCalls$UnaryServerCallHandler$UnaryServerCallListener.onHalfClose(ServerCalls.java:182)
    at io.grpc.internal.ServerCallImpl$ServerStreamListenerImpl.halfClosed(ServerCallImpl.java:355)
    at io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$1HalfClosed.runInContext(ServerImpl.java:867)
    at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
    at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:829)