11
11
12
12
namespace Addiks \DoctrineSqlAutoOptimizer \Mutators ;
13
13
14
+ use Addiks \StoredSQL \AbstractSyntaxTree \SqlAstAllColumnsSelector ;
14
15
use Addiks \StoredSQL \AbstractSyntaxTree \SqlAstColumn ;
15
- use Addiks \StoredSQL \AbstractSyntaxTree \SqlAstExpression ;
16
16
use Addiks \StoredSQL \AbstractSyntaxTree \SqlAstJoin ;
17
17
use Addiks \StoredSQL \AbstractSyntaxTree \SqlAstMutableNode ;
18
18
use Addiks \StoredSQL \AbstractSyntaxTree \SqlAstNode ;
19
- use Addiks \StoredSQL \AbstractSyntaxTree \SqlAstOperation ;
20
19
use Addiks \StoredSQL \AbstractSyntaxTree \SqlAstSelect ;
21
20
use Addiks \StoredSQL \ExecutionContext ;
22
- use Addiks \StoredSQL \Schema \Column ;
23
21
use Addiks \StoredSQL \Schema \Schemas ;
24
22
use Closure ;
25
- use Webmozart \Assert \Assert ;
26
- use Addiks \StoredSQL \AbstractSyntaxTree \SqlAstAllColumnsSelector ;
27
23
28
24
/** @psalm-import-type Mutator from SqlAstMutableNode */
29
25
final class RemovePointlessJoinsMutator
@@ -56,7 +52,7 @@ public function removePointlessJoins(
56
52
57
53
foreach ($ select ->joins () as $ join ) {
58
54
if (!$ this ->isJoinAliasUsedInSelect ($ join , $ select )
59
- && !$ this ->canJoinChangeResultSetSize ($ join , $ select , $ context )) {
55
+ && !$ join ->canJoinChangeResultSetSize ($ context )) {
60
56
$ select ->replaceJoin ($ join , null );
61
57
}
62
58
}
@@ -68,18 +64,15 @@ private function isJoinAliasUsedInSelect(SqlAstJoin $join, SqlAstSelect $select)
68
64
/** @var bool $isJoinAliasUsedInSelect */
69
65
$ isJoinAliasUsedInSelect = false ;
70
66
71
- /** @var string $joinName */
72
- $ joinName = ($ join ->alias () ?? $ join ->joinedTable ())->toSql ();
73
-
74
67
/** @var SqlAstNode $selectChildNode */
75
68
foreach ($ select ->children () as $ selectChildNode ) {
76
69
if ($ selectChildNode === $ join ) {
77
70
continue ;
78
71
}
79
72
80
- $ selectChildNode ->walk ([function (SqlAstNode $ node ) use (&$ isJoinAliasUsedInSelect , $ joinName ): void {
73
+ $ selectChildNode ->walk ([function (SqlAstNode $ node ) use (&$ isJoinAliasUsedInSelect , $ join ): void {
81
74
if ($ node instanceof SqlAstColumn) {
82
- if ($ node ->tableNameString () === $ joinName ) {
75
+ if ($ node ->tableNameString () === $ join -> aliasName () ) {
83
76
$ isJoinAliasUsedInSelect = true ;
84
77
}
85
78
@@ -101,136 +94,4 @@ private function isJoinAliasUsedInSelect(SqlAstJoin $join, SqlAstSelect $select)
101
94
102
95
return $ isJoinAliasUsedInSelect ;
103
96
}
104
-
105
- private function canJoinChangeResultSetSize (
106
- SqlAstJoin $ join ,
107
- SqlAstSelect $ select ,
108
- ExecutionContext $ context
109
- ): bool {
110
- /** @var bool $canJoinChangeResultSetSize */
111
- $ canJoinChangeResultSetSize = true ;
112
-
113
- /** @var SqlAstExpression|null $condition */
114
- $ condition = $ join ->condition ();
115
-
116
- if ($ join ->isUsingColumnCondition ()) {
117
- # "... JOIN foo USING(bar_id)"
118
-
119
- if ($ condition instanceof SqlAstColumn) {
120
- return $ this ->canUsingJoinChangeResultSetSize ($ join , $ context );
121
- }
122
-
123
- } elseif (is_object ($ condition )) {
124
- # "... JOIN foo ON(foo.id = bar.foo_id)"
125
-
126
- return $ this ->canOnJoinChangeResultSetSize ($ join , $ context );
127
- }
128
-
129
- return true ;
130
- }
131
-
132
- private function canUsingJoinChangeResultSetSize (SqlAstJoin $ join , ExecutionContext $ context ): bool
133
- {
134
- /** @var SqlAstExpression|null $column */
135
- $ column = $ join ->condition ();
136
-
137
- Assert::isInstanceOf ($ column , SqlAstColumn::class);
138
-
139
- /** @var string $columnName */
140
- $ columnName = $ column ->columnNameString ();
141
-
142
- return !$ context ->isOneToOneRelation (
143
- $ context ->findTableWithColumn ($ columnName ),
144
- $ columnName ,
145
- $ join ->joinedTable (),
146
- $ columnName
147
- );
148
- }
149
-
150
- private function canOnJoinChangeResultSetSize (SqlAstJoin $ join , ExecutionContext $ context ): bool
151
- {
152
- /** @var SqlAstExpression|null $condition */
153
- $ condition = $ join ->condition ();
154
-
155
- /** @var array<SqlAstOperation> $equations */
156
- $ equations = $ condition ->extractFundamentalEquations ();
157
-
158
- /** @var array<SqlAstOperation> $alwaysFalseEquations */
159
- $ alwaysFalseEquations = array_filter ($ equations , function (SqlAstOperation $ equation ): bool {
160
- return $ equation ->isAlwaysFalse ();
161
- });
162
-
163
- if (!empty ($ alwaysFalseEquations )) {
164
- return true ;
165
- }
166
-
167
- $ equations = array_filter ($ equations , function (SqlAstOperation $ equation ): bool {
168
- return !$ equation ->isAlwaysTrue ();
169
- });
170
-
171
- /** @var string $joinAlias */
172
- $ joinAlias = $ join ->aliasName ();
173
-
174
- foreach ($ equations as $ equation ) {
175
- /** @var SqlAstExpression $leftSide */
176
- $ leftSide = $ equation ->leftSide ();
177
-
178
- /** @var SqlAstExpression $rightSide */
179
- $ rightSide = $ equation ->rightSide ();
180
-
181
- if ($ leftSide instanceof SqlAstColumn && $ leftSide ->tableNameString () === $ joinAlias ) {
182
- /** @var SqlAstExpression $joiningSide */
183
- $ joiningSide = $ rightSide ;
184
-
185
- /** @var SqlAstExpression $joinedSide */
186
- $ joinedSide = $ leftSide ;
187
-
188
- } elseif ($ rightSide instanceof SqlAstColumn && $ rightSide ->tableNameString () === $ joinAlias ) {
189
- /** @var SqlAstExpression $joiningSide */
190
- $ joiningSide = $ leftSide ;
191
-
192
- /** @var SqlAstExpression $joinedSide */
193
- $ joinedSide = $ rightSide ;
194
-
195
- } else {
196
- # Unknown condition, let's assume that this JOIN can change result size to be safe.
197
- return true ;
198
- }
199
-
200
- if ($ joinedSide instanceof SqlAstColumn && $ joiningSide instanceof SqlAstColumn) {
201
- /** @var Column|null $joinedColumn */
202
- $ joinedColumn = $ context ->columnByNode ($ joinedSide );
203
-
204
- /** @var Column|null $joiningColumn */
205
- $ joiningColumn = $ context ->columnByNode ($ joiningSide );
206
-
207
- if (is_object ($ joinedColumn ) && is_object ($ joiningColumn )) {
208
- foreach ([
209
- [$ join ->isRightOuterJoin (), $ joiningColumn ],
210
- [$ join ->isLeftOuterJoin (), $ joinedColumn ],
211
- ] as [$ isOuterJoin , $ column ]) {
212
- if ($ isOuterJoin ) {
213
- if ($ column ->nullable () || !$ column ->unique ()) {
214
- return true ;
215
- }
216
-
217
- } else {
218
- if (!$ column ->unique ()) {
219
- return true ;
220
- }
221
- }
222
- }
223
-
224
- return false ;
225
- }
226
-
227
- } else {
228
- # Either a literal (which will change result size), or an unknown condition (which might change it).
229
- return true ;
230
- }
231
- }
232
-
233
- # All equations are either always true or always false. Either way, this JOIN changes the result size.
234
- return true ;
235
- }
236
97
}
0 commit comments