• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 com.android.server.healthconnect.storage.utils;
18 
19 import android.annotation.Nullable;
20 
21 import com.android.server.healthconnect.storage.request.ReadTableRequest;
22 
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.List;
26 import java.util.stream.Collectors;
27 
28 /** @hide */
29 public final class WhereClauses {
30     public enum LogicalOperator {
31         AND(" AND "),
32         OR(" OR ");
33 
34         private final String opKeyword;
35 
LogicalOperator(String opKeyword)36         LogicalOperator(String opKeyword) {
37             this.opKeyword = opKeyword;
38         }
39     }
40 
41     private final List<String> mClauses = new ArrayList<>();
42     private final LogicalOperator mLogicalOperator;
43 
WhereClauses(LogicalOperator logicalOperator)44     public WhereClauses(LogicalOperator logicalOperator) {
45         mLogicalOperator = logicalOperator;
46     }
47 
48 
addWhereBetweenClause(String columnName, long start, long end)49     public WhereClauses addWhereBetweenClause(String columnName, long start, long end) {
50         mClauses.add(columnName + " >= " + start + " AND " + columnName + " < " + end);
51 
52         return this;
53     }
54 
55     /**
56      * Adds a clause for a time column between two values.
57      *
58      * <p>If the clause would be guaranteed empty (end < 0 or start > end) no clause is added.
59      *
60      * @param columnName the column to check (or no effect if null)
61      * @param startTime the earliest start time (inclusive)
62      * @param endTime the latest end time (inclusive)
63      */
addWhereBetweenTimeClause( @ullable String columnName, long startTime, long endTime)64     public WhereClauses addWhereBetweenTimeClause(
65             @Nullable String columnName, long startTime, long endTime) {
66         if (columnName == null || endTime < 0 || endTime < startTime) {
67             // Below method has check for startTime less than 0.
68             // If both startTime and endTime are less than 0 then no new time clause will be added
69             // and just return current object.
70             return addWhereLaterThanTimeClause(columnName, startTime);
71         }
72 
73         mClauses.add(columnName + " >= " + startTime + " AND " + columnName + " < " + endTime);
74 
75         return this;
76     }
77 
78     /**
79      * Adds a clause for a time column after a given time.
80      *
81      * <p>If the clause would be guaranteed empty (startTime < 0) then no clause is added.
82      *
83      * @param columnName the column to check (or no effect if null)
84      * @param startTime the earliest start time (inclusive)
85      */
addWhereLaterThanTimeClause(@ullable String columnName, long startTime)86     public WhereClauses addWhereLaterThanTimeClause(@Nullable String columnName, long startTime) {
87         if (startTime < 0 || columnName == null) {
88             return this;
89         }
90 
91         mClauses.add(columnName + " > " + startTime);
92 
93         return this;
94     }
95 
addWhereInClause(String columnName, List<String> values)96     public WhereClauses addWhereInClause(String columnName, List<String> values) {
97         if (values == null || values.isEmpty()) return this;
98 
99         mClauses.add(columnName + " IN " + "('" + String.join("', '", values) + "')");
100 
101         return this;
102     }
103 
104     /**
105      * Adds a clause for a value contained in a list of values.
106      *
107      * <p>If the clause would be guaranteed empty (values null or emoty) no clause is added.
108      *
109      * @param columnName the column to check (or no effect if null)
110      * @param values the column to check (or no effect if null)
111      */
addWhereInClauseWithoutQuotes( @ullable String columnName, @Nullable List<String> values)112     public WhereClauses addWhereInClauseWithoutQuotes(
113             @Nullable String columnName, @Nullable List<String> values) {
114         if (columnName == null || values == null || values.isEmpty()) return this;
115 
116         mClauses.add(columnName + " IN " + "(" + String.join(", ", values) + ")");
117 
118         return this;
119     }
120 
addWhereEqualsClause(String columnName, String value)121     public WhereClauses addWhereEqualsClause(String columnName, String value) {
122         if (columnName == null || value == null || value.isEmpty() || columnName.isEmpty()) {
123             return this;
124         }
125 
126         mClauses.add(columnName + " = " + StorageUtils.getNormalisedString(value));
127         return this;
128     }
129 
addWhereGreaterThanClause(String columnName, String value)130     public WhereClauses addWhereGreaterThanClause(String columnName, String value) {
131         mClauses.add(columnName + " > '" + value + "'");
132 
133         return this;
134     }
135 
136     /** Add clause columnName > value */
addWhereGreaterThanClause(String columnName, long value)137     public WhereClauses addWhereGreaterThanClause(String columnName, long value) {
138         mClauses.add(columnName + " > " + value);
139 
140         return this;
141     }
142 
addWhereGreaterThanOrEqualClause(String columnName, long value)143     public WhereClauses addWhereGreaterThanOrEqualClause(String columnName, long value) {
144         mClauses.add(columnName + " >= " + value);
145 
146         return this;
147     }
148 
addWhereLessThanOrEqualClause(String columnName, long value)149     public WhereClauses addWhereLessThanOrEqualClause(String columnName, long value) {
150         mClauses.add(columnName + " <= " + value);
151 
152         return this;
153     }
154 
155     /** Add clause columnName < value */
addWhereLessThanClause(String columnName, long value)156     public WhereClauses addWhereLessThanClause(String columnName, long value) {
157         mClauses.add(columnName + " < " + value);
158 
159         return this;
160     }
161 
162     /**
163      * Adds where in condition for the column.
164      *
165      * @param columnName Column name on which where condition to be applied
166      * @param values to check in the where condition
167      */
addWhereInIntsClause(String columnName, Collection<Integer> values)168     public WhereClauses addWhereInIntsClause(String columnName, Collection<Integer> values) {
169         if (values == null || values.isEmpty()) return this;
170 
171         mClauses.add(
172                 columnName
173                         + " IN ("
174                         + values.stream().map(String::valueOf).collect(Collectors.joining(", "))
175                         + ")");
176 
177         return this;
178     }
179 
180     /**
181      * Adds where in condition for the column.
182      *
183      * @param columnName Column name on which where condition to be applied
184      * @param values to check in the where condition
185      */
addWhereInLongsClause( @ullable String columnName, @Nullable Collection<Long> values)186     public WhereClauses addWhereInLongsClause(
187             @Nullable String columnName, @Nullable Collection<Long> values) {
188         if (columnName == null || values == null || values.isEmpty()) return this;
189 
190         mClauses.add(
191                 columnName
192                         + " IN ("
193                         + values.stream()
194                                 .distinct()
195                                 .map(String::valueOf)
196                                 .collect(Collectors.joining(", "))
197                         + ")");
198 
199         return this;
200     }
201 
202     /**
203      * Creates IN clause, where in range is another SQL request. Returns instance with extra clauses
204      * set.
205      */
addWhereInSQLRequestClause(String columnName, ReadTableRequest inRequest)206     public WhereClauses addWhereInSQLRequestClause(String columnName, ReadTableRequest inRequest) {
207         mClauses.add(columnName + " IN (" + inRequest.getReadCommand() + ") ");
208 
209         return this;
210     }
211 
212     /** Adds other {@link WhereClauses} as conditions of this where clause. */
addNestedWhereClauses(WhereClauses... otherWhereClauses)213     public WhereClauses addNestedWhereClauses(WhereClauses... otherWhereClauses) {
214         for (WhereClauses whereClauses : otherWhereClauses) {
215             if (whereClauses.mClauses.isEmpty()) {
216                 // skip empty WhereClauses so we don't add empty pairs of parentheses "()" to the
217                 // final SQL statement
218                 continue;
219             }
220             if (mLogicalOperator.equals(whereClauses.mLogicalOperator)) {
221                 // If the logical operator matches we don't need extra parentheses
222                 mClauses.addAll(whereClauses.mClauses);
223             } else {
224                 mClauses.add("(" + whereClauses.get(/* withWhereKeyword= */ false) + ")");
225             }
226         }
227 
228         return this;
229     }
230 
231     /**
232      * Returns where clauses joined by 'AND', if the input parameter isIncludeWHEREinClauses is true
233      * then the clauses are preceded by 'WHERE'.
234      */
get(boolean withWhereKeyword)235     public String get(boolean withWhereKeyword) {
236         if (mClauses.isEmpty()) {
237             return "";
238         }
239 
240         return (withWhereKeyword ? " WHERE " : "")
241                 + String.join(mLogicalOperator.opKeyword, mClauses);
242     }
243 }
244