ontop icon indicating copy to clipboard operation
ontop copied to clipboard

"MinorOntopInternalBugException: Was expecting a unique and known DB term type ..." possibly related to `DBIfElseNullFunctionSymbol` and/or use of iriSafeConstraints

Open fracorco opened this issue 5 months ago • 0 comments

Here is a simple test case reproducing the issue, attached as Docker Compose stack using the latest ontop/ontop-5.4.0-latest image along with PostgreSQL:

ontop-issue.zip

Given:

  1. the following DB schema:

    CREATE TABLE address (
        municipality VARCHAR NOT NULL,
        street VARCHAR NOT NULL,
        housenumber VARCHAR NOT NULL,
        building_id VARCHAR, -- optional
        PRIMARY KEY (municipality, street, housenumber)
    );
    
  2. the following lenses configuration, basically stating that all columns are IRI safe:

    {
        "relations": [{
            "type": "BasicLens",
            "name": ["lens", "address"],
            "baseRelation": ["address"],
            "iriSafeConstraints": {
                "added": ["municipality", "street", "housenumber", "building_id"]
            }
        }]
    }
    
  3. the following mapping (excerpt, formatted for clarity), which basically extract address instances and, where available, their association to building instances:

    mappingId   address
    target      :address/{municipality}/{street}/{housenumber} a ex:Address.
                :building/{municipality}/{building_id} a ex:Building; ex:hasAddress :address/{municipality}/{street}/{housenumber}.
    source      SELECT * FROM lens.address
    
  4. a minimal ontology just declaring employed classes and properties (not really needed)

  5. the following SPARQL query (prefixes omitted, available in the Ontop portal at http://localhost:8080/, under tab "Non-Functioning Queries"):

    SELECT ?address ?building
    {
        ?address a ex:Address .
        OPTIONAL { ?building ex:hasAddress ?address }
    }
    

Executing the query leads to this exception:

it.unibz.inf.ontop.exception.MinorOntopInternalBugException: Was expecting a unique and known DB term type to be extracted for the SQL variable v3
  at it.unibz.inf.ontop.generation.DefaultSQLIQTree2NativeNodeGenerator.lambda$extractUniqueKnownType$6(DefaultSQLIQTree2NativeNodeGenerator.java:77)
  at java.base/java.util.Optional.orElseThrow(Unknown Source)
  at it.unibz.inf.ontop.generation.DefaultSQLIQTree2NativeNodeGenerator.extractUniqueKnownType(DefaultSQLIQTree2NativeNodeGenerator.java:77)
  at it.unibz.inf.ontop.generation.DefaultSQLIQTree2NativeNodeGenerator.lambda$extractVariableTypeMap$2(DefaultSQLIQTree2NativeNodeGenerator.java:69)
  at it.unibz.inf.ontop.utils.ImmutableCollectors.lambda$toMap$0(ImmutableCollectors.java:110)
  at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(Unknown Source)
  at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Unknown Source)
  at java.base/java.util.stream.AbstractPipeline.copyInto(Unknown Source)
  at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
  at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(Unknown Source)
  at java.base/java.util.stream.AbstractPipeline.evaluate(Unknown Source)
  at java.base/java.util.stream.ReferencePipeline.collect(Unknown Source)
  at it.unibz.inf.ontop.generation.DefaultSQLIQTree2NativeNodeGenerator.extractVariableTypeMap(DefaultSQLIQTree2NativeNodeGenerator.java:67)
  at it.unibz.inf.ontop.generation.DefaultSQLIQTree2NativeNodeGenerator.generate(DefaultSQLIQTree2NativeNodeGenerator.java:54)
  at it.unibz.inf.ontop.answering.reformulation.generation.impl.SQLGeneratorImpl.generateNativeNode(SQLGeneratorImpl.java:231)
  at it.unibz.inf.ontop.answering.reformulation.generation.impl.SQLGeneratorImpl.generateSourceQuery(SQLGeneratorImpl.java:105)
  at it.unibz.inf.ontop.answering.reformulation.generation.impl.SQLGeneratorImpl.generateSourceQuery(SQLGeneratorImpl.java:83)
  at it.unibz.inf.ontop.answering.reformulation.generation.impl.SQLGeneratorImpl.generateSourceQuery(SQLGeneratorImpl.java:78)
  at it.unibz.inf.ontop.answering.reformulation.impl.QuestQueryProcessor.generateExecutableQuery(QuestQueryProcessor.java:158)
  at it.unibz.inf.ontop.answering.reformulation.impl.QuestQueryProcessor.reformulateIntoNativeQuery(QuestQueryProcessor.java:122)
  at it.unibz.inf.ontop.answering.connection.impl.QuestStatement.executeSelectQuery(QuestStatement.java:130)
  at it.unibz.inf.ontop.answering.connection.impl.QuestStatement.executeSelectQuery(QuestStatement.java:124)
  at it.unibz.inf.ontop.answering.connection.impl.QuestStatement.lambda$execute$2(QuestStatement.java:207)
  at it.unibz.inf.ontop.answering.connection.impl.QuestStatement$QueryExecutionThread.run(QuestStatement.java:107)

For information, this is the IQ for the corresponded planned query:

CONSTRUCT
    [address, building]
    [
        address/RDF(
            kg:address/{}/{}/{}(
                VARCHARToTEXT(DECLARE_IRI_SAFE(municipality0)),
                VARCHARToTEXT(DECLARE_IRI_SAFE(street1)),
                VARCHARToTEXT(DECLARE_IRI_SAFE(housenumber2))
            ),
            IRI
        ),
        building/RDF(
            kg:building/{}/{}(
                VARCHARToTEXT(IF_ELSE_NULL(IS_NOT_NULL(building_id3),DECLARE_IRI_SAFE(municipality0))),
                VARCHARToTEXT(DECLARE_IRI_SAFE(building_id3))
            ),
            IF_ELSE_NULL(IS_NOT_NULL(building_id3),IRI)
        )
    ]
    EXTENSIONAL "address"(0:municipality0,1:street1,2:housenumber2,3:building_id3)

And this is the last IQ before the conversion to the native query fails (right after "New query after the dialect-specific extra normalization"):

CONSTRUCT
    [v0, v1, v2, v3, v4, building_id3]
    [
        v0/DECLARE_IRI_SAFE(municipality0),
        v1/DECLARE_IRI_SAFE(street1),
        v2/DECLARE_IRI_SAFE(housenumber2),
        v3/IF_ELSE_NULL(IS_NOT_NULL(building_id3),DECLARE_IRI_SAFE(municipality0)),
        v4/DECLARE_IRI_SAFE(building_id3)
    ]
    EXTENSIONAL "address"(0:municipality0,1:street1,2:housenumber2,3:building_id3)

The error message seems to suggest a problem in determining the DBTermType for variable v3 starting from expression IF_ELSE_NULL(IS_NOT_NULL(building_id3),DECLARE_IRI_SAFE(municipality0)).

Note that:

  1. The error does not occur if OPTIONAL is removed from the original query. In this case, the IQ after "dialect-specific extra normalization" is the following:

    CONSTRUCT
        [v0, v1, v2, v3, v4]
        [
            v0/DECLARE_IRI_SAFE(municipality0),
            v1/DECLARE_IRI_SAFE(street1),
            v2/DECLARE_IRI_SAFE(housenumber2),
            v3/DECLARE_IRI_SAFE(municipality0),
            v4/DECLARE_IRI_SAFE(building_id3)
        ]
        FILTER IS_NOT_NULL(building_id3)
            EXTENSIONAL "address"(0:municipality0,1:street1,2:housenumber2,3:building_id3)
    
  2. The error does not occur if column municipality is not declared as IRI safe in the lenses file. In this case, the IQ after "dialect-specific extra normalization" is the following

    CONSTRUCT
        [municipality1m3, v0, v1, building_id2, v2]
        [
            v0/DECLARE_IRI_SAFE(street0),
            v1/DECLARE_IRI_SAFE(housenumber1),
            v2/DECLARE_IRI_SAFE(building_id2)
        ]
        EXTENSIONAL "address"(0:municipality1m3,1:street0,2:housenumber1,3:building_id2)
    

In both aforementioned working cases, IF_ELSE_NULL is not present. Could there be some issue in inferType related to DBIfElseNullFunctionSymbol and sub-classes, whose use is somehow triggered here by the use of IRI safe constraints?

Provided I'm not familiar with the code and the intended Ontop behavior in these cases, another thing I find useful to point out regards whether it's really needed to introduce that IF_ELSE_NULL (already in the planned query). If column building_id is NULL, then template kg:building/{municipality}/{building_id} cannot be evaluated independently of whether municipality is NULL or not, so why forcing municipality to also be null when building_id is, via IF_ELSE_NULL(IS_NOT_NULL(building_id3),DECLARE_IRI_SAFE(municipality0))?

HTH

P.S. when setting up the test case, it seems that Ontop running with --dev (ONTOP_DEV_MODE=TRUE) detects changes to the lenses file, but does not reload itself.

fracorco avatar Aug 14 '25 10:08 fracorco