cayenne-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Erlend Birkenes ...@dataloy.com>
Subject Problem with joint prefetch and flattened attributes
Date Tue, 05 Aug 2014 11:07:49 GMT
Hi

I discovered a problem with joint prefetch and flattened attributes. This
is in 3.1B2 which is the version we use at the moment. Not sure if this is
the lates or not..

If I have a entity A with a normal relationship to an entity B, and B has
flattened attributes from C via toCdbrel.SOME_PROPERTY.
This works fine normally. But when using joint prefetch the table aliases
in the query will be wrong. (Querying A and prefetching B).
If there are multiple flattened attributes, C will also be joined in
multiple times.

The problem is in SelectTranslator#appendQueryColumns.
This section adds columns from the target entity of a joint prefetch,
including relationships for flattened attributes:

while (targetObjAttrs.hasNext()) {
        ObjAttribute oa = targetObjAttrs.next();
        Iterator<CayenneMapEntry> dbPathIterator = oa.getDbPathIterator();
        while (dbPathIterator.hasNext()) {
                Object pathPart = dbPathIterator.next();

                if (pathPart == null) {
                        throw new CayenneRuntimeException(
                                        "ObjAttribute has no component: " +
oa.getName());
                }
                else if (pathPart instanceof DbRelationship) {
                        DbRelationship rel = (DbRelationship) pathPart;
                        dbRelationshipAdded(rel, JoinType.INNER, null);
//PROBLEM
                }
                else if (pathPart instanceof DbAttribute) {
                        DbAttribute attribute = (DbAttribute) pathPart;

                        appendColumn(columns, oa, attribute, attributes,
labelPrefix
                                        + '.'
                                        + attribute.getName());
                }
        }
}

The problem is that dbRelationshipAdded sets the topNode of the JoinStack
to the new node when a relationship is added and this is never reset in
this case. So every time a relationship is added in this loop it will add a
node to the previously added node. This will increase the tableAlias by 1
for every relationship, and also cause any attributes directly on B to
belong to the wrong node and therefore get the wrong tableAlias. It will
also cause an additional join, but joined to the wrong table.

The topNode of the JoinStack needs to be reset to the correct value after
handing each attribute, or more presisely: after the "while
(dbPathIterator.hasNext())" loop the topNode needs to be the same as it was
before the loop, because we are still processing attributes on the same
entity (B in this case).

Storing the current topNode, and resetting it after processing each
attribute fixed the problem for me. Like this:

while (targetObjAttrs.hasNext()) {
                    JoinTreeNode topNode = getJoinStack().topNode;  // FIX
                    ObjAttribute oa = targetObjAttrs.next();
                    Iterator<CayenneMapEntry> dbPathIterator =
oa.getDbPathIterator();
                    while (dbPathIterator.hasNext()) {
                        Object pathPart = dbPathIterator.next();

                        if (pathPart == null) {
                            throw new CayenneRuntimeException(
                                    "ObjAttribute has no component: " +
oa.getName());
                        }
                        else if (pathPart instanceof DbRelationship) {
                            DbRelationship rel = (DbRelationship) pathPart;
                            dbRelationshipAdded(rel, JoinType.INNER, null);
                        }
                        else if (pathPart instanceof DbAttribute) {
                            DbAttribute attribute = (DbAttribute) pathPart;

                            appendColumn(columns, oa, attribute,
attributes, labelPrefix
                                    + '.'
                                    + attribute.getName());
                        }
                    }
                    getJoinStack().topNode = topNode; // FIX
                }

resetJoinStack() can't be used in this case, since we don't want the
rootNode.

I've run the Cayenne tests after this change with no errors, but I've only
done limited testing in our own app so far.

This is clearly a bug. Should I create a JIRA issue?


–
Erlend <http://www.dataloy-systems.com>

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message