1/** 2 * @file Describe the file 3 * Copyright (c) 2023 Huawei Device Co., Ltd. 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 17import data_rdb from '@ohos.data.relationalStore'; 18import dataSharePredicates from '@ohos.data.dataSharePredicates'; 19import { BusinessError } from '@ohos.base'; 20import { ValueType } from '@ohos.data.ValuesBucket'; 21 22import { Log } from '@ohos/common/src/main/ets/utils/Log'; 23import { hasNoError } from '@ohos/common/src/main/ets/utils/ErrorUtils'; 24import { getBundleNameByUri } from '@ohos/common/src/main/ets/utils/UrlUtils'; 25 26import { Calendars } from '@ohos/datastructure/src/main/ets/calendars/Calendars'; 27import { AppName } from '@ohos/datastructure/src/main/ets/calendars/CalendarsColumns'; 28import { CalendarsIndexes, parseCalendarsIndexes } from '@ohos/datastructure/src/main/ets/calendars/CalendarsIndexes'; 29import { CalendarsColumns } from '@ohos/datastructure/src/main/ets/calendars/CalendarsColumns'; 30import { EventColumns, UPDATE_INSTANCES_COLUMNS } from '@ohos/datastructure/src/main/ets/events/EventColumns'; 31import { Events } from '@ohos/datastructure/src/main/ets/events/Events'; 32import { EventIndexes, parseIndexes } from '@ohos/datastructure/src/main/ets/events/EventIndexes'; 33import { parseCalendars } from '@ohos/datastructure/src/main/ets/calendars/CalendarsParser'; 34import { parseEvents } from '@ohos/datastructure/src/main/ets/events/EventParser'; 35 36import { ErrorCode } from '../../constants/ErrorCode'; 37import { DefaultProcessor } from './../DefaultProcessor'; 38import { initValueCreator, initPredicateCreator, deleteValueCreator } from '../DatabaseProcessorHelper'; 39import { acquireExpandOne, isNeedRefreshInstances, acquireUpdateOne } from '../instances/InstancesProcessor'; 40import { filterValuesBucket } from '@ohos/common/src/main/ets/utils/ValuesUtils'; 41 42const TAG: string = 'EventsProcessor'; 43const CHANNEL_ID: string = 'channel_id'; 44 45/** 46 * the Events table processor 47 * 48 * @since 2022-06-24 49 */ 50export class EventsProcessor extends DefaultProcessor { 51 async insertByHighAuthority(rdbStore: data_rdb.RdbStore, uri: string, 52 value: data_rdb.ValuesBucket, callback: Function) { 53 let values = filterValuesBucket(value, CHANNEL_ID); 54 const callerName = getBundleNameByUri(uri); 55 initValueCreator(values, callerName); 56 const isCalendarExist = await isCalendarContainSameId(rdbStore, values); 57 if (isCalendarExist) { 58 this.insertEventWithInstanceExpand(rdbStore, uri, values, callback); 59 } else { 60 Log.warn(TAG, 'not support insert operation'); 61 const err: BusinessError = { 62 code: ErrorCode.UN_SUPPORT_OPERATION, 63 name: 'UnSupportedOperationException', 64 message: 'The calling application cannot insert an event without its own calendar' 65 }; 66 callback(err, -1); 67 } 68 } 69 70 async insertByLowAuthority(rdbStore: data_rdb.RdbStore, uri: string, 71 value: data_rdb.ValuesBucket, callback: Function) { 72 let values = filterValuesBucket(value, CHANNEL_ID); 73 const callerName = getBundleNameByUri(uri); 74 initValueCreator(values, callerName); 75 const isCalendarCreatorExist = await isCalendarContainSameCreator(rdbStore, values); 76 if (isCalendarCreatorExist) { 77 this.insertEventWithInstanceExpand(rdbStore, uri, values, callback); 78 } else { 79 Log.warn(TAG, 'not support insert operation'); 80 const err: BusinessError = { 81 code: ErrorCode.UN_SUPPORT_OPERATION, 82 name: 'UnSupportedOperationException', 83 message: 'The calling application cannot insert an event with different creator from calendar' 84 }; 85 callback(err, -1); 86 } 87 } 88 89 async deleteByLowAuthority(rdbStore: data_rdb.RdbStore, uri: string, 90 predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 91 const callerName = getBundleNameByUri(uri); 92 initPredicateCreator(predicates, callerName); 93 this.doDelete(rdbStore, uri, predicates, callback); 94 } 95 96 async updateByHighAuthority(rdbStore: data_rdb.RdbStore, uri: string, value: data_rdb.ValuesBucket, 97 predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 98 let values = filterValuesBucket(value, CHANNEL_ID); 99 deleteValueCreator(values); 100 this.updateEventWithInstance(rdbStore, uri, values, predicates, callback); 101 } 102 103 async updateByLowAuthority(rdbStore: data_rdb.RdbStore, uri: string, value: data_rdb.ValuesBucket, 104 predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 105 let values = filterValuesBucket(value, CHANNEL_ID); 106 const callerName = getBundleNameByUri(uri); 107 deleteValueCreator(values); 108 initPredicateCreator(predicates, callerName); 109 this.updateEventWithInstance(rdbStore, uri, values, predicates, callback); 110 } 111 112 async queryByLowAuthority(rdbStore: data_rdb.RdbStore, uri: string, columns: Array<string>, 113 predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 114 const callerName = getBundleNameByUri(uri); 115 initPredicateCreator(predicates, callerName); 116 this.doQuery(rdbStore, uri, columns, predicates, callback); 117 } 118 119 /** 120 * 插入 Event 并扩展该 Event 的实例 Instance 121 * 122 * @param rdbStore rdb数据库 123 * @param uri DataShare的uri 124 * @param values 插入的数据 125 * @param callback 回调方法 126 */ 127 async insertEventWithInstanceExpand(rdbStore: data_rdb.RdbStore, uri: string, 128 values: data_rdb.ValuesBucket, callback: Function) { 129 this.doInsert(rdbStore, uri, values, (err: BusinessError, rowId: number) => { 130 Log.debug(TAG, `before insert callback`); 131 if (hasNoError(err)) { 132 acquireExpandOne(rdbStore, rowId, values); 133 } 134 callback(err, rowId); 135 Log.debug(TAG, `after insert callback`); 136 }); 137 } 138 139 /** 140 * 更新 Event,并同步更新 Instance 141 * 142 * @param rdbStore rdb数据库 143 * @param uri DataShare的uri 144 * @param values 更新的数据 145 * @param predicates 更新条件 146 * @param callback 回调方法 147 */ 148 async updateEventWithInstance(rdbStore: data_rdb.RdbStore, uri: string, values: data_rdb.ValuesBucket, 149 predicates: dataSharePredicates.DataSharePredicates, callback: Function) { 150 let needUpdatedEventIds: Array<number>; 151 if (isNeedRefreshInstances(values)) { 152 needUpdatedEventIds = await queryEventIdsByPredicate(rdbStore, predicates); 153 } 154 this.doUpdate(rdbStore, uri, values, predicates, (err: BusinessError, count: Function) => { 155 Log.debug(TAG, `before update callback`); 156 if (hasNoError(err)) { 157 updateInstances(rdbStore, needUpdatedEventIds); 158 } 159 callback(err, count); 160 Log.debug(TAG, `after update callback`); 161 }) 162 } 163} 164 165/** 166 * 检查待插入的 event 与 calendar 表中相同 calendar_id 的元组是否拥有相同的 creator 167 * 168 * @param rdbStore rdb数据库 169 * @param values 插入操作的数据 170 * @return true 相同 false 不相同 171 */ 172async function isCalendarContainSameCreator(rdbStore: data_rdb.RdbStore, 173 values: data_rdb.ValuesBucket): Promise<boolean> { 174 Log.debug(TAG, 'isCalendarContainSameCreator start'); 175 const eventCreator = values[EventColumns.CREATOR]; 176 let resultSet = await queryCalendarIdAndCreatorByEvent(rdbStore, values); 177 if (resultSet === null || resultSet === undefined) { 178 return false; 179 } 180 const calendarsIndexes: CalendarsIndexes | undefined = parseCalendarsIndexes(resultSet); 181 if (resultSet.goToFirstRow()) { 182 let calendars: Calendars | undefined = parseCalendars(resultSet, calendarsIndexes); 183 if (calendars === null || calendars === undefined) { 184 return false; 185 } 186 if (calendars.creator === eventCreator || calendars.creator === AppName.CALENDARDATA) { 187 return true; 188 } 189 } 190 return false; 191} 192 193/** 194 * 检查待插入的 event 与 calendar 表中是否存在相同 calendar_id 的元组 195 * 196 * @param rdbStore rdb数据库 197 * @param values 插入操作的数据 198 * @return true 相同 false 不相同 199 */ 200async function isCalendarContainSameId(rdbStore: data_rdb.RdbStore, 201 values: data_rdb.ValuesBucket): Promise<boolean> { 202 Log.debug(TAG, 'isCalendarContainSameId start'); 203 let resultSet = await queryCalendarIdAndCreatorByEvent(rdbStore, values); 204 if (resultSet === null || resultSet === undefined) { 205 return false; 206 } 207 if (resultSet.rowCount > 0) { 208 return true; 209 } 210 return false; 211} 212 213/** 214 * 查询待插入的 event 数据中 calendar_id 与 calendar 表相同的结果 215 * 216 * @param rdbStore rdb数据库 217 * @param values 插入操作的数据 218 * @return DataShareResultSet 219 */ 220async function queryCalendarIdAndCreatorByEvent(rdbStore: data_rdb.RdbStore, 221 values: data_rdb.ValuesBucket): Promise<data_rdb.ResultSet> { 222 const calendarId = values[EventColumns.CALENDAR_ID] as ValueType; 223 const columns = [CalendarsColumns.ID, CalendarsColumns.CREATOR]; 224 let predicates = new dataSharePredicates.DataSharePredicates(); 225 predicates.equalTo(CalendarsColumns.ID, calendarId); 226 let resultSet: data_rdb.ResultSet = {} as data_rdb.ResultSet; 227 try { 228 resultSet = await rdbStore.query(CalendarsColumns.TABLE_NAME, predicates, columns); 229 } catch (err) { 230 Log.error(TAG, 'Calendars query data error'); 231 } 232 return resultSet; 233} 234 235/** 236 * 根据Predicate查出待刷新的Events ID列表,用于后续刷新Instances的依据 237 * 238 * @param rdbStore rdb数据库 239 * @param predicates update条件,用于定位需要刷新Instances对应eventId的范围 240 */ 241async function queryEventIdsByPredicate(rdbStore: data_rdb.RdbStore, 242 predicates: dataSharePredicates.DataSharePredicates): Promise<number[]> { 243 const resultArray: number[] = []; 244 const columns = [EventColumns.ID]; 245 try { 246 const resultSet = await rdbStore.query(EventColumns.TABLE_NAME, predicates, columns); 247 if (resultSet === null || resultSet === undefined) { 248 Log.error(TAG, 'queryEventIdsByPredicate get invalid resultSet'); 249 return resultArray; 250 } 251 if (resultSet.rowCount === 0 || !resultSet.goToFirstRow()) { 252 Log.info(TAG, `queryEventIdsByPredicate row count:${resultSet?.rowCount}`); 253 return resultArray; 254 } 255 do { 256 const eventId = resultSet.getLong(resultSet.getColumnIndex(EventColumns.ID)); 257 resultArray.push(eventId); 258 } while (resultSet.goToNextRow()); 259 } catch (err) { 260 Log.error(TAG, `queryEventIdsByPredicate get err: ${err?.message}`); 261 } 262 return resultArray; 263} 264 265/** 266 * 批量刷新Instances 267 * 268 * @param rdbStore rdb数据库 269 * @param updatedIds 待刷新的eventId列表 270 */ 271async function updateInstances(rdbStore: data_rdb.RdbStore, updatedIds: Array<number>) { 272 if (updatedIds === null || updatedIds === undefined || updatedIds.length === 0) { 273 return; 274 } 275 276 // 1.先批量查询出全部Events数据 277 const eventsMap: Map<number, Events> = await queryEventsByIds(rdbStore, updatedIds); 278 279 if (eventsMap === null || eventsMap === undefined || eventsMap.size === 0) { 280 Log.warn(TAG, 'updateInstances with invalid eventsMap'); 281 return; 282 } 283 284 // 2.再遍历生成Instances 285 for (let eventId of updatedIds) { 286 // 基于eventId单条刷新Instances,需传入events字段值数据 287 acquireUpdateOne(rdbStore, eventId, eventsMap.get(eventId)); 288 } 289} 290 291/** 292 * 根据EventIds查询出Events数据,用于生成Instances,先批量查询避免在for循环中查询 293 * 294 * @param rdbStore rdb数据库 295 * @param eventIds 需查询的eventIds集合 296 * @return Map<number, Events> eventId-Events的map 297 */ 298async function queryEventsByIds(rdbStore: data_rdb.RdbStore, 299 eventIds: Array<number>): Promise<Map<number, Events>> { 300 const resultMap: Map<number, Events> = new Map(); 301 const predicate = new dataSharePredicates.DataSharePredicates(); 302 predicate.in(EventColumns.ID, eventIds); 303 const columns = UPDATE_INSTANCES_COLUMNS; 304 columns.push(EventColumns.ID); 305 columns.push(EventColumns.CREATOR); 306 try { 307 const resultSet = await rdbStore.query(EventColumns.TABLE_NAME, predicate, columns); 308 if (resultSet === null || resultSet === undefined) { 309 Log.error(TAG, 'queryEventsByIds get invalid resultSet'); 310 return resultMap; 311 } 312 if (!resultSet.goToFirstRow()) { 313 Log.error(TAG, `queryEventsByIds goto first row failed ${resultSet?.rowCount}`); 314 return resultMap; 315 } 316 const indexes: EventIndexes | undefined = parseIndexes(resultSet); 317 do { 318 const event: Events | undefined = parseEvents(resultSet, indexes); 319 if (event != undefined) { 320 resultMap.set(event.id, event); 321 } 322 } while (resultSet.goToNextRow()); 323 } catch (err) { 324 Log.error(TAG, `queryEventsByIds get err ${err?.message}`); 325 } 326 return resultMap; 327}