CAY-2863 DbEntity qualifiers are no longer applied to JOIN conditions
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java
index 1b16a44..05498e9 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/DbPathProcessor.java
@@ -61,6 +61,13 @@
             return;
         }
 
+        // special case when the path should be processed in the context of the current join clause
+        if(TableTree.CURRENT_ALIAS.equals(next)) {
+            entity = context.getTableTree().nonNullActiveNode().getEntity();
+            appendCurrentPath(next);
+            return;
+        }
+
         throw new IllegalStateException("Unable to resolve path: " + currentDbPath.toString() + "." + next);
     }
 
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java
index d6369e1..f01578e 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java
@@ -296,8 +296,9 @@
             return new EmptyNode();
         } else {
             String alias = context.getTableTree().aliasForPath(result.getLastAttributePath());
+            // special case when the path should be processed in the context of the current join clause
             if(TableTree.CURRENT_ALIAS.equals(alias)) {
-                alias = node.getPathAliases().get(TableTree.CURRENT_ALIAS);
+                alias = context.getTableTree().nonNullActiveNode().getTableAlias();
             }
             return table(alias).column(result.getLastAttribute()).build();
         }
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java
index fe573f6..276282c 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTree.java
@@ -37,6 +37,7 @@
 class TableTree {
 
     public static final String CURRENT_ALIAS = "__current_table_alias__";
+    public static final CayennePath CURRENT_ALIAS_PATH = CayennePath.of("__current_table_alias__");
 
     /**
      * Tables mapped by db path it's spawned by.
@@ -50,6 +51,7 @@
     private final TableTree parentTree;
     private final TableTreeNode rootNode;
 
+    private TableTreeNode activeNode;
     private int tableAliasSequence;
 
     TableTree(DbEntity root, TableTree parentTree) {
@@ -64,10 +66,6 @@
     }
 
     void addJoinTable(CayennePath path, DbRelationship relationship, JoinType joinType, Expression additionalQualifier) {
-        // skip adding new node if we are resolving table tree itself
-        if(path.marker() == CayennePath.TABLE_TREE_MARKER) {
-            return;
-        }
         TableTreeNode treeNode = tableNodes.get(path);
         if (treeNode != null) {
             return;
@@ -78,13 +76,13 @@
     }
 
     String aliasForPath(CayennePath attributePath) {
-        // should be resolved dynamically by the caller
-        if(attributePath.marker() == CayennePath.TABLE_TREE_MARKER) {
-            return CURRENT_ALIAS;
-        }
         if(attributePath.isEmpty()) {
             return rootNode.getTableAlias();
         }
+        // should be resolved dynamically by the caller
+        if(CURRENT_ALIAS_PATH.equals(attributePath)) {
+            return CURRENT_ALIAS;
+        }
         TableTreeNode node = tableNodes.get(attributePath);
         if (node == null) {
             throw new CayenneRuntimeException("No table for attribute '%s' found", attributePath);
@@ -100,6 +98,17 @@
         return 't' + String.valueOf(tableAliasSequence++);
     }
 
+    TableTreeNode nonNullActiveNode() {
+        if(activeNode == null) {
+            throw new CayenneRuntimeException("No active TableTree node found");
+        }
+        return activeNode;
+    }
+
+    void setActiveNode(TableTreeNode activeNode) {
+        this.activeNode = activeNode;
+    }
+
     public int getNodeCount() {
         return tableNodes.size() + 1;
     }
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeStage.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeStage.java
index c1a9e4c..d297e32 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeStage.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TableTreeStage.java
@@ -95,7 +95,10 @@
         }
 
         dbQualifier = translateToDbPath(node, dbQualifier);
+        // mark table tree node as current to process qualifier
+        context.getTableTree().setActiveNode(node);
         Node translatedQualifier = context.getQualifierTranslator().translate(dbQualifier);
+        context.getTableTree().setActiveNode(null);
         return joinBuilder.and(() -> translatedQualifier);
     }
 
@@ -104,9 +107,14 @@
         dbQualifier = dbQualifier.transform(input -> {
             // here we are not only marking path, but changing ObjPath to DB
             if (input instanceof ASTPath) {
-                ASTDbPath dbPath = new ASTDbPath(pathToRoot.dot(((ASTPath) input).getPath()).withMarker(CayennePath.TABLE_TREE_MARKER));
-                dbPath.setPathAliases(Map.of(TableTree.CURRENT_ALIAS, node.getTableAlias()));
-                return dbPath;
+                // we do not really care about the parent path, as we do not need to join any new table here.
+                // so we must tell the path processor that we are processing exactly this table
+                // TODO: should check qualifiers via related tables if that is even the thing
+                CayennePath path = ((ASTPath) input).getPath();
+                if(!pathToRoot.isEmpty()) {
+                    path = TableTree.CURRENT_ALIAS_PATH.dot(path);
+                }
+                return new ASTDbPath(path);
             }
             return input;
         });
diff --git a/cayenne/src/main/java/org/apache/cayenne/exp/path/CayennePath.java b/cayenne/src/main/java/org/apache/cayenne/exp/path/CayennePath.java
index 562db04..3d402d6 100644
--- a/cayenne/src/main/java/org/apache/cayenne/exp/path/CayennePath.java
+++ b/cayenne/src/main/java/org/apache/cayenne/exp/path/CayennePath.java
@@ -58,11 +58,6 @@
     int PREFETCH_MARKER = 1;
 
     /**
-     * Marker denotes paths inside tree resolution logic
-     */
-    int TABLE_TREE_MARKER = 2;
-
-    /**
      * Constant value for an empty path
      */
     CayennePath EMPTY_PATH = new EmptyCayennePath(NO_MARKER);
diff --git a/cayenne/src/test/java/org/apache/cayenne/CDOQualifiedEntitiesIT.java b/cayenne/src/test/java/org/apache/cayenne/CDOQualifiedEntitiesIT.java
index 68b7227..7449d07 100644
--- a/cayenne/src/test/java/org/apache/cayenne/CDOQualifiedEntitiesIT.java
+++ b/cayenne/src/test/java/org/apache/cayenne/CDOQualifiedEntitiesIT.java
@@ -101,9 +101,13 @@
     private void createJoinDataSet() throws Exception {
         tQualified3.insert(1, "O1", null);
         tQualified3.insert(2, "O2", accessStackAdapter.supportsBoolean() ? true : 1);
+        tQualified3.insert(3, "11", null);
+        tQualified3.insert(4, "12", accessStackAdapter.supportsBoolean() ? true : 1);
 
         tQualified4.insert(1, "SHOULD_SELECT", null, 1);
         tQualified4.insert(2, "SHOULD_NOT_SELECT", null, 2);
+        tQualified4.insert(3, "SHOULD_SELECT_TOO", null, 3);
+        tQualified4.insert(4, "SHOULD_NOT_SELECT_TOO", null, 4);
     }
 
     @Test
@@ -230,6 +234,20 @@
     }
 
     @Test
+    public void joinWithQualifierAndAliases() throws Exception {
+        createJoinDataSet();
+
+        List<Qualified4> result = ObjectSelect.query(Qualified4.class)
+                .where(Qualified4.QUALIFIED3.alias("a1").dot(Qualified3.NAME).like("O%"))
+                .or(Qualified4.QUALIFIED3.alias("a2").dot(Qualified3.NAME).like("1%"))
+                .select(context);
+
+        assertEquals(2, result.size());
+        assertEquals("SHOULD_SELECT", result.get(0).getName());
+        assertEquals("SHOULD_SELECT_TOO", result.get(1).getName());
+    }
+
+    @Test
     public void joinWithCustomDbQualifier() throws Exception {
         createJoinDataSet();