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