Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix invalid sql generated for CTI JOINs when INNER JOIN'ing with a WITH clause #6475

Closed
wants to merge 3 commits into from

Conversation

stesie
Copy link
Contributor

@stesie stesie commented May 27, 2017

See #6464 for a detailed example of the problem.

For a query like

$qb
            ->select('p', 'a')
            ->from(GH6464Post::class, 'p')
            ->innerJoin(GH6464Author::class, 'a', 'WITH', 'p.authorId = a.id')
            ->getQuery();

... where GH6464Author is a CTI entity, the sql generation currently generates two JOINs (correct) and attaches both join conditions to the latter JOIN. This works with MySQL and SQLite, however fails on PostgreSQL.

This patch

  • adds a functional test case that actually tries to run such a query
  • applies a fix to SqlWalker that makes it output a nested JOIN
  • adapts test cases in SelectSqlGenerationTest to reflect the change (and make those SQL statements valid -- on PostgreSQL)

@stesie
Copy link
Contributor Author

stesie commented May 27, 2017

Test fails seem to be related to changes to symfony/yaml and unrelated to this change (as tests are green on PHP 7.0 and on 7.1 with low dependencies)

@Majkl578
Copy link
Contributor

Can you please rebase this PR onto current master? It should fix build errors on Travis. 👍

@stesie
Copy link
Contributor Author

stesie commented Jul 16, 2017

@Majkl578 rebased, tests are green now

... but scrutinizer fails now. Yet the only issue now caused by my additions (https://scrutinizer-ci.com/g/doctrine/doctrine2/inspections/de206627-1966-4ae5-9710-e883754a9c58/issues/files/tests/Doctrine/Tests/ORM/Functional/Ticket/GH6464Test.php) is a false positive as getSql returns a string (as opposed to reported array) ...

*
* @return string
*/
public function walkRangeVariableDeclaration($rangeVariableDeclaration)
public function walkRangeVariableDeclaration($rangeVariableDeclaration, $buildNestedJoins = false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The additional parameter is problematic. The decision is one in the SqlWalker anyway, so the correct solution (IMO) would be to move the method body to a private method, then keep the public API as-is and delegate the call on the public API to the private API, where the new parameter is defined.

/** @Column(type="integer") */
public $authorId;

/** @Column(length=100) */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop any details that aren't strictly needed. If columns aren't affected, they shouldn't be in here

public function testIssue()
{

$qb = $this->_em->createQueryBuilder();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please either inline with the code below or use a DQL string instead of the builder

@@ -891,7 +892,11 @@ public function walkRangeVariableDeclaration($rangeVariableDeclaration)
);

if ($class->isInheritanceTypeJoined()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this block be moved to a private method too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I doubt that aids readability since the decision is made by the caller (actually even that one's caller) and not the extracted function itself ... and it just modifies local state (i.e. $sql).

@@ -1137,11 +1142,11 @@ public function walkJoin($join)
$conditions[] = '(' . $this->walkConditionalExpression($join->conditionalExpression) . ')';
}

$condExprConjunction = ($class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER)
$condExprConjunction = ($class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER && empty($conditions))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The empty($conditions) is used twice - can you give it a name and assign it to a meaningful variable?

@Ocramius Ocramius added the Bug label Jul 22, 2017
@Ocramius Ocramius added this to the 2.5.7 milestone Jul 31, 2017
@Ocramius Ocramius changed the title Fix invalid sql generated for CTI JOINs when INNER JOIN'ing Fix invalid sql generated for CTI JOINs when INNER JOIN'ing with a WITH clause Aug 11, 2017
public function testIssue()
{
$query = $this->_em->createQueryBuilder()
->select('p', 'a')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sort of select is invalid - removing a from the selection, as you should NEVER fetch-join results over an association that was filtered.

Ocramius added a commit that referenced this pull request Aug 11, 2017
Ocramius added a commit that referenced this pull request Aug 11, 2017
@Ocramius Ocramius closed this in 6d428c9 Aug 11, 2017
@Ocramius
Copy link
Member

Manually merged, thanks @stesie!

master: 6d428c9
2.5: d89d238

@ivok
Copy link

ivok commented Dec 3, 2020

Why this issue is not fixed yet?

For our new project we use Doctrine 2 and MSSQL and with first join query we encountered the same problem.

When I dump the sql build from Doctrine it wraps the joined table name with parenthesis "JOIN (table tb) ON...." which gives error for MSSQL

In our case, we have Users table which should be joined to the table we are quering. The User entity is softedeletable, which adds condition in the join clause.

But becouse of this condition the SqlWalker wraps the table users with parenthesis here:1084

// If we have WITH condition, we need to build nested joins for target class table and cti joins if ($withCondition) { $sql .= '(' . $targetTableJoin['table'] . $ctiJoins . ') ON ' . $targetTableJoin['condition']; } else { $sql .= $targetTableJoin['table'] . ' ON ' . $targetTableJoin['condition'] . $ctiJoins; }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants