"MinorOntopInternalBugException: Was expecting a unique and known DB term type ..." possibly related to `DBIfElseNullFunctionSymbol` and/or use of iriSafeConstraints
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:
Given:
-
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) ); -
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"] } }] } -
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 -
a minimal ontology just declaring employed classes and properties (not really needed)
-
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:
-
The error does not occur if
OPTIONALis 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) -
The error does not occur if column
municipalityis not declared as IRI safe in the lenses file. In this case, the IQ after "dialect-specific extra normalization" is the followingCONSTRUCT [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.