1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.room.parser.optimization
18 
19 import androidx.room.parser.ParsedQuery
20 import androidx.room.parser.SqlParser
21 import androidx.room.processor.QueryRewriter
22 import androidx.room.solver.query.result.QueryResultAdapter
23 
24 /**
25  * If the query response has unused columns, this rewrites the query to only fetch those columns.
26  *
27  * e.g. if it is a query like `SELECT * FROM User` where only `name` and `lastName` columns are
28  * accessed in the generated code, this re-writer will change it to `SELECT name, lastName FROM
29  * (SELECT * FROM User)`. Sqlite takes care of the rest where it flattens the query to avoid
30  * fetching unused columns in intermediate steps.
31  */
32 object RemoveUnusedColumnQueryRewriter : QueryRewriter {
rewritenull33     override fun rewrite(query: ParsedQuery, resultAdapter: QueryResultAdapter): ParsedQuery {
34         // cannot do anything w/o a result info
35         val resultInfo = query.resultInfo ?: return query
36         if (resultAdapter.mappings.isEmpty()) {
37             return query
38         }
39         val usedColumns = resultAdapter.mappings.flatMap { it.usedColumns }
40         val columnNames = resultInfo.columns.map { it.name }
41         val unusedColumns = columnNames - usedColumns
42         if (unusedColumns.isEmpty()) {
43             return query // nothing to optimize here
44         }
45         if (columnNames.size != columnNames.distinct().size) {
46             // if result has duplicate columns, ignore for now
47             return query
48         }
49         val usedColumnNames = columnNames - unusedColumns
50         val updated =
51             SqlParser.parse(
52                 "SELECT ${usedColumnNames.joinToString(", ") { "`$it`" }} FROM (${query.original})"
53             )
54         if (updated.errors.isNotEmpty()) {
55             // we somehow messed up, return original
56             return query
57         }
58         return updated
59     }
60 }
61