CMIS Query with a "null predicate" containing a "multi-valued-column reference" throws exception
A CMIS Query with a “null predicate” containing a “multi-valued-column reference” throws an exception. A sample query that demonstrates the problem is listed below as is the exception stack trace. Please confirm this is a bug that I should record in JIRA.
SELECT cmis:objectId FROM cmis:document WHERE dc:subjects IS NOT NULL
Stack trace:
curl -u Administrator:Administrator "http://demo.nuxeo.com/nuxeo/atom/cmis/default/query?q=SELECT+cmis:objectId+FROM+cmis:document+WHERE+dc:subjects+IS+NOT+NULL&searchAllVersions=true&includeRelationships=none"
org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException: Failed to execute query: CMISQL: SELECT cmis:objectId FROM cmis:document WHERE dc:subjects IS NOT NULL: ERROR: missing FROM-clause entry for table "dc_subjects"
Position: 331
at org.nuxeo.ecm.core.opencmis.impl.server.NuxeoCmisService.query(NuxeoCmisService.java:1342)
...
Caused by: org.nuxeo.ecm.core.api.ClientException: Failed to execute query: CMISQL: SELECT cmis:objectId FROM cmis:document WHERE dc:subjects IS NOT NULL: ERROR: missing FROM-clause entry for table "dc_subjects"
Position: 331
at org.nuxeo.ecm.core.api.AbstractSession.queryAndFetch(AbstractSession.java:1672)
at sun.reflect.GeneratedMethodAccessor286.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.nuxeo.ecm.core.api.TransactionalCoreSessionWrapper.invoke(TransactionalCoreSessionWrapper.java:136)
at com.sun.proxy.$Proxy177.queryAndFetch(Unknown Source)
at org.nuxeo.ecm.core.opencmis.impl.server.NuxeoCmisService.query(NuxeoCmisService.java:1295)
... 57 more
Caused by: org.nuxeo.ecm.core.query.QueryException: Invalid query: CMISQL: SELECT cmis:objectId FROM cmis:document WHERE dc:subjects IS NOT NULL
at org.nuxeo.ecm.core.storage.sql.coremodel.SQLSession$SQLSessionQuery.executeAndFetch(SQLSession.java:613)
at org.nuxeo.ecm.core.storage.sql.coremodel.SQLSession.queryAndFetch(SQLSession.java:527)
at org.nuxeo.ecm.core.api.AbstractSession.queryAndFetch(AbstractSession.java:1667)
... 63 more
Caused by: org.nuxeo.ecm.core.storage.StorageException: Invalid query: CMISQL: SELECT cmis:objectId FROM cmis:document WHERE dc:subjects IS NOT NULL
at org.nuxeo.ecm.core.storage.sql.jdbc.JDBCMapper.queryAndFetch(JDBCMapper.java:898)
at org.nuxeo.ecm.core.storage.sql.SoftRefCachingMapper.queryAndFetch(SoftRefCachingMapper.java:107)
at org.nuxeo.ecm.core.storage.sql.SessionImpl.queryAndFetch(SessionImpl.java:1209)
at org.nuxeo.ecm.core.storage.sql.ra.ConnectionImpl.queryAndFetch(ConnectionImpl.java:354)
at org.nuxeo.ecm.core.storage.sql.coremodel.SQLSession$SQLSessionQuery.executeAndFetch(SQLSession.java:610)
... 65 more
Caused by: org.postgresql.util.PSQLException: ERROR: missing FROM-clause entry for table "dc_subjects"
Position: 331
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2157)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1886)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:555)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:417)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:302)
at org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
at org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
at org.nuxeo.ecm.core.storage.sql.jdbc.ResultSetQueryResult.<init>(ResultSetQueryResult.java:74)
at org.nuxeo.ecm.core.storage.sql.jdbc.JDBCMapper.queryAndFetch(JDBCMapper.java:894)
... 69 more
I believe it's a bug but on the other hand the current code is not intended to work with null predicates and multi-valued properties. I'm not sure we can find an efficient way to do that. Please open a JIRA anyway.
WHERE clauses with (dc:subjects IS NULL OR dc:subjects NOT IN ('foo')) will likely be common. It seems reasonable to explicitly look for these types of multi-valued column ref OR clauses and optimize them with NOT EXISTS (SELECT 1 FROM … WHERE … IN …) sub-queries. Does this seem reasonable?
Please discuss in the ticket the exact semantics you imagine for dc:subjects IS NULL OR ANY dc:subjects NOT IN ('foo')
because I'm not sure the spec matches your expectation. The CMIS 1.1 spec says (§2.1.14.2.4.3) that ANY dc:subjects NOT IN ('foo')
evaluates to TRUE if at least one subject is not 'foo'.
If you want something to be optimized into NOT EXISTS (SELECT 1 ... WHERE ... dc_subjects.item = 'foo')
then it would be dc:subjects IS NULL OR NOT ANY dc:subjects IN ('foo')
.