Skip to content

Commit facc558

Browse files
committed
Fix check_access_control_on_utilized_columns_only for CTE
Previously we weren't associating the with query with the columns used from it in the main part of the query, so we wouldn't consider any columns as used. When the usage within the cte or outside of it was just an identifier, we would have the utilized column collected anyway because the column collected in the main query kept the source table information. However, if there was an expression in between, we would lose that information, and we wouldn't check access control for the column that was used in an expression in the cte. Example: For the following query ``` with cte as (select x as c1, z + 1 as c2 from t13) select c1, c2 from (select * from cte) ``` Previously we would only check access permissions on t13.x, but not t13.z. With this change we will check column access on both 13.x and t13.z.
1 parent 56bf5e6 commit facc558

File tree

3 files changed

+77
-8
lines changed

3 files changed

+77
-8
lines changed

presto-analyzer/src/main/java/com/facebook/presto/sql/analyzer/RelationId.java

+4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ public boolean isAnonymous()
5353
return sourceNode == null;
5454
}
5555

56+
public Node getSourceNode()
57+
{
58+
return sourceNode;
59+
}
5660
@Override
5761
public boolean equals(Object o)
5862
{

presto-main/src/main/java/com/facebook/presto/sql/analyzer/UtilizedColumnsAnalyzer.java

+46-8
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.facebook.presto.sql.tree.Lateral;
3838
import com.facebook.presto.sql.tree.Node;
3939
import com.facebook.presto.sql.tree.NodeRef;
40+
import com.facebook.presto.sql.tree.QualifiedName;
4041
import com.facebook.presto.sql.tree.Query;
4142
import com.facebook.presto.sql.tree.QuerySpecification;
4243
import com.facebook.presto.sql.tree.Relation;
@@ -48,7 +49,10 @@
4849
import com.facebook.presto.sql.tree.Union;
4950
import com.facebook.presto.sql.tree.Unnest;
5051
import com.facebook.presto.sql.tree.Values;
52+
import com.facebook.presto.sql.tree.With;
53+
import com.facebook.presto.sql.tree.WithQuery;
5154
import com.google.common.collect.HashMultimap;
55+
import com.google.common.collect.ImmutableList;
5256
import com.google.common.collect.ImmutableSet;
5357

5458
import java.util.HashMap;
@@ -57,28 +61,29 @@
5761
import java.util.Map.Entry;
5862
import java.util.Set;
5963

64+
import static com.google.common.collect.ImmutableList.toImmutableList;
6065
import static com.google.common.collect.Sets.intersection;
6166
import static java.lang.String.format;
6267

6368
/**
6469
* Finds all utilized columns in the query. Utilized columns are those that would have an "impact" on the query's results.
65-
*
70+
* <p>
6671
* For example, in the query:
67-
* SELECT nationkey FROM (SELECT * FROM nation WHERE name = 'USA')
72+
* SELECT nationkey FROM (SELECT * FROM nation WHERE name = 'USA')
6873
* Even though all the columns in table nation are referenced by the query (in the SELECT * part), only the columns
6974
* "name" and "nationkey" have an "impact" on the query's results.
70-
*
75+
* <p>
7176
* The high-level algorithm works as follows:
7277
* 1. Find all fields referenced in all clauses of the outermost SELECT query, and add them to an explore list.
7378
* 2. For each field reference F in the explore list, find its referenced relation R.
7479
* 3. If R is a SELECT query:
75-
* a. Find the SELECT item expression that F references. Add all fields referenced by that expression to the explore list.
76-
* b. Add all fields referenced by every other clause of the SELECT query to the explore list.
80+
* a. Find the SELECT item expression that F references. Add all fields referenced by that expression to the explore list.
81+
* b. Add all fields referenced by every other clause of the SELECT query to the explore list.
7782
* 4. Otherwise,
78-
* a. Add F's referenced field to a referenced fields list.
79-
* b. For each child of R, find the corresponding child of F, and add it to the explore list.
83+
* a. Add F's referenced field to a referenced fields list.
84+
* b. For each child of R, find the corresponding child of F, and add it to the explore list.
8085
* 5. Repeat from step 2 for all fields in the explore list, until all have been resolved to a base table relation.
81-
*
86+
* <p>
8287
* The referenced fields list at the end of this algorithm will contain all the columns referenced by the query, that impact the output.
8388
* Step 3a is where fields that do not impact the output are pruned.
8489
*/
@@ -271,6 +276,23 @@ protected Void visitQuerySpecification(QuerySpecification querySpec, Context con
271276
return null;
272277
}
273278

279+
@Override
280+
protected Void visitWith(With node, Context context)
281+
{
282+
ImmutableList.copyOf(node.getQueries()).reverse().forEach(query -> process(query, context));
283+
284+
return null;
285+
}
286+
287+
@Override
288+
protected Void visitWithQuery(WithQuery withQuery, Context context)
289+
{
290+
context.copyFieldIdsToExploreForWithQuery(withQuery);
291+
process(withQuery.getQuery(), context);
292+
293+
return null;
294+
}
295+
274296
@Override
275297
protected Void visitSampledRelation(SampledRelation sampledRelation, Context context)
276298
{
@@ -493,5 +515,21 @@ private void addFieldIdToExplore(FieldId fieldId)
493515
{
494516
fieldsToExplore.put(fieldId.getRelationId(), fieldId);
495517
}
518+
519+
// Associate the relation from the with clause with the fieldIdsToExplore that we collected for it
520+
// when processing the main part of the query
521+
public void copyFieldIdsToExploreForWithQuery(WithQuery withQuery)
522+
{
523+
QualifiedName name = QualifiedName.of(withQuery.getName().getValue());
524+
List<RelationId> relationIds = fieldsToExplore.keySet().stream()
525+
.filter(key -> key.getSourceNode() instanceof Table && ((Table) key.getSourceNode()).getName().equals(name))
526+
.collect(toImmutableList());
527+
if (relationIds.size() != 1) {
528+
throw new UnsupportedOperationException("Multiple relations with the same name are not supported by UtilizedColumnAnalyzer");
529+
}
530+
fieldsToExplore.putAll(
531+
RelationId.of(withQuery.getQuery().getQueryBody()),
532+
fieldsToExplore.get(relationIds.get(0)));
533+
}
496534
}
497535
}

presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestUtilizedColumnsAnalyzer.java

+27
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,33 @@ public void testUDF()
519519
ImmutableMap.of(QualifiedObjectName.valueOf("tpch.s1.t1"), ImmutableSet.of("a")));
520520
}
521521

522+
@Test
523+
public void testCteWithExpressionInSelect()
524+
{
525+
assertUtilizedTableColumns(
526+
"with cte as (select x as c1, y as c2, z + 1 as c3 from t13) select c1, c3 from (select * from cte)",
527+
ImmutableMap.of(QualifiedObjectName.valueOf("tpch.s1.t13"), ImmutableSet.of("x", "z")));
528+
}
529+
530+
@Test
531+
public void testMultipleCtes()
532+
{
533+
assertUtilizedTableColumns(
534+
"with cte1 as (select x as c1, y as c2, z + 1 as c3 from t13), cte2 as (select c1 + 1 as a, c3 as b from cte1) select a, b from (select * from cte2)",
535+
ImmutableMap.of(QualifiedObjectName.valueOf("tpch.s1.t13"), ImmutableSet.of("x", "z")));
536+
assertUtilizedTableColumns(
537+
"with cte1 as (select x as c1, z + 1 as c2, y from t13), cte2 as (select c1 + 1 c3, c2 +1 as c4 from cte1) select c3 from cte2",
538+
ImmutableMap.of(QualifiedObjectName.valueOf("tpch.s1.t13"), ImmutableSet.of("x")));
539+
}
540+
541+
@Test
542+
public void testMultipleCtesWithSameNameFallsBackToAllColumns()
543+
{
544+
assertUtilizedTableColumns(
545+
"with cte1 as (select x + 1 as c1, y as c2, z + 1 as c3 from t13), cte2 as (with cte1 AS (select y +1 as c1 from t13) select * from cte1) SELECT cte1.c1, cte2.c1 from cte1 join cte2 on cte1.c1=cte2.c1",
546+
ImmutableMap.of(QualifiedObjectName.valueOf("tpch.s1.t13"), ImmutableSet.of("x", "y", "z")));
547+
}
548+
522549
private void assertUtilizedTableColumns(@Language("SQL") String query, Map<QualifiedObjectName, Set<String>> expected)
523550
{
524551
transaction(transactionManager, accessControl)

0 commit comments

Comments
 (0)