DyadicExpression "OR" compiles into nested SQL expressions which exhausts the stack of the underlying DB


yunus@...
 

Hi everyone,

I use JDOQL with Datanucleus 4 and Spanner as backend. I use Spanner as a backend for some time and it works mostly fine. But recently I hit a problem when there are too many OR statements (~2K in my case).
The OR statements are merged by nesting. Each of them is separated via parentheses inside another one. As a result, we have a deeply nested statement and database raises an exception that the query exhausts the nesting limits. 

How can I configure Datanucleus to get rid of these heavily nested queries? See below for more explanation.

A query like: 
SELECT FROM PartitionPrivilege WHERE partition.table.tableName == t1 && partition.table.database.name == t2 && (partition.partitionName == p0 || partition.partitionName == p1 || partition.partitionName == p2 || ............ partition.partitionName == p1823 )
Compiled as many nested DyadicExpressions:
[filter:DyadicExpression{DyadicExpression{DyadicExpression{PrimaryExpression{partition.table.tableName}  =  ParameterExpression{t1}}  AND  DyadicExpression{PrimaryExpression{partition.table.database.name}  =  ParameterExpression{t2}}}  AND  DyadicExpression{DyadicExpression{DyadicExpression{DyadicExpression{DyadicExpression{DyadicExpression{DyadicExpression{DyadicExpression{DyadicExpression{DyadicExpression{DyadicExpression{DyadicExpression{DyadicExpression{DyadicExpression{DyadicExpression{DyadicExpression{ ................................................ DyadicExpression{PrimaryExpression{partition.partitionName}  =  ParameterExpression{p0}}  OR  DyadicExpression{PrimaryExpression{partition.partitionName}  =  ParameterExpression{p1}}}  OR  DyadicExpression{PrimaryExpression{partition.partitionName}  =  ParameterExpression{p2}}}  OR  DyadicExpression{PrimaryExpression{partition.partitionName}  =  ParameterExpression{p3}}}  OR  DyadicExpression{PrimaryExpression{partition.partitionName}  =  ParameterExpression{p4}}}  OR .....................
Finally, compiled query is:
Query compiled to datastore query "SELECT DISTINCT 'PartitionPrivilege' AS NUCLEUS_TYPE,A0.CREATE_TIME,A0.GRANT_OPTION,A0.GRANTOR,A0.GRANTOR_TYPE,A0.PRINCIPAL_NAME,A0.PRINCIPAL_TYPE,A0.PART_PRIV,A0.PART_GRANT_ID FROM PART_PRIVS A0 LEFT OUTER JOIN PARTITIONS B0 ON A0.PART_ID = B0.PART_ID LEFT OUTER JOIN TBLS C0 ON B0.TBL_ID = C0.TBL_ID LEFT OUTER JOIN DBS D0 ON C0.DB_ID = D0.DB_ID WHERE C0.TBL_NAME = ? AND D0.`NAME` = ? AND ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((B0.PART_NAME = ?) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?)) OR (B0.PART_NAME = ?))

Thanks in advance
yunus


Andy
 
Edited

What SQL do you want to generate??
The JDOQL you provided has been mapped directly as you requested it.

You could try having a single parameter of type Collection that has multiple String values, and then do
:param.contains(partition.partitionName)
which will probably compile to an IN expression. Clearly you then may have a limit in your RDBMS about the number of values specifiable in an IN expression. Can't see what you would expect an ORM to do about that, you could split up your query to have fewer values in a query and aggregate multiple queries


yunus@...
 

Thanks Andy, "contains" has worked. It uses the "IN" statement.

I have been confused about the behavior of the "OR" statements. I have expected something like  (x OR y OR z) instead it is (((X) OR Y) OR Z). I've thought that I miss a flag/parameter that causes this. But I understand that it is the expected behavior.