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.request; 18 19 import static com.android.server.healthconnect.storage.utils.StorageUtils.DELIMITER; 20 import static com.android.server.healthconnect.storage.utils.StorageUtils.LIMIT_SIZE; 21 import static com.android.server.healthconnect.storage.utils.WhereClauses.LogicalOperator.AND; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.health.connect.Constants; 26 import android.util.Slog; 27 28 import com.android.server.healthconnect.storage.TransactionManager; 29 import com.android.server.healthconnect.storage.datatypehelpers.RecordHelper; 30 import com.android.server.healthconnect.storage.utils.OrderByClause; 31 import com.android.server.healthconnect.storage.utils.SqlJoin; 32 import com.android.server.healthconnect.storage.utils.WhereClauses; 33 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.Objects; 37 38 /** 39 * A request for {@link TransactionManager} to read the DB 40 * 41 * @hide 42 */ 43 public class ReadTableRequest { 44 private static final String TAG = "HealthConnectRead"; 45 private static final String UNION_ALL = " UNION ALL "; 46 47 private final String mTableName; 48 private RecordHelper<?> mRecordHelper; 49 private List<String> mColumnNames; 50 private SqlJoin mJoinClause; 51 private WhereClauses mWhereClauses = new WhereClauses(AND); 52 private boolean mDistinct = false; 53 private OrderByClause mOrderByClause = new OrderByClause(); 54 private String mLimitClause = ""; 55 private List<ReadTableRequest> mExtraReadRequests; 56 private List<ReadTableRequest> mUnionReadRequests; 57 58 @SuppressWarnings("NullAway.Init") // TODO(b/317029272): fix this suppression ReadTableRequest(@onNull String tableName)59 public ReadTableRequest(@NonNull String tableName) { 60 Objects.requireNonNull(tableName); 61 62 mTableName = tableName; 63 } 64 getRecordHelper()65 public RecordHelper<?> getRecordHelper() { 66 return mRecordHelper; 67 } 68 setRecordHelper(RecordHelper<?> recordHelper)69 public ReadTableRequest setRecordHelper(RecordHelper<?> recordHelper) { 70 mRecordHelper = recordHelper; 71 return this; 72 } 73 setColumnNames(@onNull List<String> columnNames)74 public ReadTableRequest setColumnNames(@NonNull List<String> columnNames) { 75 Objects.requireNonNull(columnNames); 76 77 mColumnNames = columnNames; 78 return this; 79 } 80 setWhereClause(WhereClauses whereClauses)81 public ReadTableRequest setWhereClause(WhereClauses whereClauses) { 82 mWhereClauses = whereClauses; 83 return this; 84 } 85 86 /** Used to set Join Clause for the read query */ 87 @NonNull setJoinClause(SqlJoin joinClause)88 public ReadTableRequest setJoinClause(SqlJoin joinClause) { 89 mJoinClause = joinClause; 90 return this; 91 } 92 93 /** 94 * Use this method to enable the Distinct clause in the read command. 95 * 96 * <p><b>NOTE: make sure to use the {@link ReadTableRequest#setColumnNames(List)} to set the 97 * column names to be used as the selection args.</b> 98 */ 99 @NonNull setDistinctClause(boolean isDistinctValuesRequired)100 public ReadTableRequest setDistinctClause(boolean isDistinctValuesRequired) { 101 mDistinct = isDistinctValuesRequired; 102 return this; 103 } 104 105 /** Returns SQL statement to perform read operation. */ 106 @NonNull getReadCommand()107 public String getReadCommand() { 108 StringBuilder builder = new StringBuilder("SELECT "); 109 if (mDistinct) { 110 builder.append("DISTINCT "); 111 builder.append(getColumnsToFetch()); 112 } else { 113 builder.append(getColumnsToFetch()); 114 } 115 builder.append(" FROM "); 116 builder.append(mTableName); 117 118 builder.append(mWhereClauses.get(/* withWhereKeyword */ true)); 119 builder.append(mOrderByClause.getOrderBy()); 120 builder.append(mLimitClause); 121 122 String readQuery = builder.toString(); 123 if (mJoinClause != null) { 124 readQuery = mJoinClause.getJoinWithQueryCommand(readQuery); 125 } 126 127 if (Constants.DEBUG) { 128 Slog.d(TAG, "read query: " + readQuery); 129 } 130 131 if (mUnionReadRequests != null && !mUnionReadRequests.isEmpty()) { 132 builder = new StringBuilder(); 133 for (ReadTableRequest unionReadRequest : mUnionReadRequests) { 134 builder.append("SELECT * FROM ("); 135 builder.append(unionReadRequest.getReadCommand()); 136 builder.append(")"); 137 builder.append(UNION_ALL); 138 } 139 140 builder.append(readQuery); 141 142 return builder.toString(); 143 } 144 145 return readQuery; 146 } 147 148 /** Get requests for populating extra data */ 149 @Nullable getExtraReadRequests()150 public List<ReadTableRequest> getExtraReadRequests() { 151 return mExtraReadRequests; 152 } 153 154 /** Sets requests to populate extra data */ setExtraReadRequests(List<ReadTableRequest> extraDataReadRequests)155 public ReadTableRequest setExtraReadRequests(List<ReadTableRequest> extraDataReadRequests) { 156 mExtraReadRequests = new ArrayList<>(extraDataReadRequests); 157 return this; 158 } 159 160 /** Get table name of the request */ getTableName()161 public String getTableName() { 162 return mTableName; 163 } 164 165 /** Sets order by clause for the read query */ 166 @NonNull setOrderBy(OrderByClause orderBy)167 public ReadTableRequest setOrderBy(OrderByClause orderBy) { 168 mOrderByClause = orderBy; 169 return this; 170 } 171 172 /** Sets LIMIT size for the read query */ 173 @NonNull setLimit(int limit)174 public ReadTableRequest setLimit(int limit) { 175 mLimitClause = LIMIT_SIZE + limit; 176 return this; 177 } 178 getColumnsToFetch()179 private String getColumnsToFetch() { 180 if (mColumnNames == null || mColumnNames.isEmpty()) { 181 return "*"; 182 } 183 184 return String.join(DELIMITER, mColumnNames); 185 } 186 187 /** Sets union read requests. */ 188 @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression setUnionReadRequests( @ullable List<ReadTableRequest> unionReadRequests)189 public ReadTableRequest setUnionReadRequests( 190 @Nullable List<ReadTableRequest> unionReadRequests) { 191 mUnionReadRequests = unionReadRequests; 192 193 return this; 194 } 195 } 196