1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #define LOG_TAG "TransactionProxy"
17 #include "napi_transaction.h"
18
19 #include "js_df_manager.h"
20 #include "js_utils.h"
21 #include "napi_async_call.h"
22 #include "napi_rdb_error.h"
23 #include "napi_rdb_js_utils.h"
24 #include "napi_rdb_predicates.h"
25 #include "napi_result_set.h"
26 #include "rdb_common.h"
27 #include "rdb_errno.h"
28 using namespace OHOS::Rdb;
29 using namespace OHOS::NativeRdb;
30 using namespace OHOS::AppDataMgrJsKit;
31 namespace OHOS::RelationalStoreJsKit {
32 #define ASSERT_RETURN_SET_ERROR(assertion, paramError) \
33 CHECK_RETURN_CORE(assertion, SetError(paramError), ERR)
34
35 struct TransactionContext : public ContextBase {
GetInstanceOHOS::RelationalStoreJsKit::TransactionContext36 void GetInstance(napi_value self)
37 {
38 auto status = napi_unwrap(env_, self, reinterpret_cast<void **>(&boundObj));
39 if (status != napi_ok || boundObj == nullptr) {
40 LOG_ERROR("TransactionProxy native instance is nullptr! code:%{public}d!", status);
41 return;
42 }
43 transaction_ = reinterpret_cast<TransactionProxy *>(boundObj)->GetInstance();
44 }
StealTransactionOHOS::RelationalStoreJsKit::TransactionContext45 std::shared_ptr<NativeRdb::Transaction> StealTransaction()
46 {
47 auto trans = std::move(transaction_);
48 transaction_ = nullptr;
49 return trans;
50 }
51 int32_t ParseRdbPredicatesProxy(napi_env env, napi_value arg, std::shared_ptr<RdbPredicates> &predicates);
52 int32_t ParseSendableValuesBucket(napi_env env, napi_value arg, ValuesBucket &valuesBucket);
53 int32_t ParseValuesBucket(napi_env env, napi_value arg, ValuesBucket &valuesBucket);
54 int32_t ParseValuesBuckets(napi_env env, napi_value arg, std::vector<ValuesBucket> &valuesBuckets);
55 int32_t ParseValuesBuckets(napi_env env, napi_value arg, ValuesBuckets &valuesBuckets);
56 int32_t ParseConflictResolution(napi_env env, napi_value arg, NativeRdb::ConflictResolution &conflictResolution);
57 std::shared_ptr<NativeRdb::Transaction> transaction_ = nullptr;
58 static constexpr int32_t KEY_INDEX = 0;
59 static constexpr int32_t VALUE_INDEX = 1;
60 };
61
ParseRdbPredicatesProxy(napi_env env,napi_value arg,std::shared_ptr<RdbPredicates> & predicates)62 int32_t TransactionContext::ParseRdbPredicatesProxy(
63 napi_env env, napi_value arg, std::shared_ptr<RdbPredicates> &predicates)
64 {
65 RdbPredicatesProxy *predicatesProxy = nullptr;
66 auto status = napi_unwrap(env, arg, reinterpret_cast<void **>(&predicatesProxy));
67 ASSERT_RETURN_SET_ERROR(status == napi_ok && predicatesProxy != nullptr,
68 std::make_shared<ParamError>("predicates", "an RdbPredicates."));
69 predicates = predicatesProxy->GetInstance();
70 ASSERT_RETURN_SET_ERROR(predicates != nullptr, std::make_shared<ParamError>("predicates", "an RdbPredicates."));
71 return OK;
72 }
73
ParseSendableValuesBucket(const napi_env env,const napi_value map,ValuesBucket & valuesBucket)74 int32_t TransactionContext::ParseSendableValuesBucket(
75 const napi_env env, const napi_value map, ValuesBucket &valuesBucket)
76 {
77 uint32_t length = 0;
78 napi_status status = napi_map_get_size(env, map, &length);
79 auto error = std::make_shared<ParamError>("ValuesBucket is invalid.");
80 ASSERT_RETURN_SET_ERROR(status == napi_ok && length > 0, error);
81 napi_value entries = nullptr;
82 status = napi_map_get_entries(env, map, &entries);
83 ASSERT_RETURN_SET_ERROR(status == napi_ok, std::make_shared<InnerError>("napi_map_get_entries failed."));
84 for (uint32_t i = 0; i < length; ++i) {
85 napi_value iter = nullptr;
86 status = napi_map_iterator_get_next(env, entries, &iter);
87 ASSERT_RETURN_SET_ERROR(status == napi_ok, std::make_shared<InnerError>("napi_map_iterator_get_next failed."));
88 napi_value values = nullptr;
89 status = napi_get_named_property(env, iter, "value", &values);
90 ASSERT_RETURN_SET_ERROR(
91 status == napi_ok, std::make_shared<InnerError>("napi_get_named_property value failed."));
92 napi_value key = nullptr;
93 status = napi_get_element(env, values, KEY_INDEX, &key);
94 ASSERT_RETURN_SET_ERROR(status == napi_ok, std::make_shared<InnerError>("napi_get_element key failed."));
95 std::string keyStr = JSUtils::Convert2String(env, key);
96 napi_value value = nullptr;
97 status = napi_get_element(env, values, VALUE_INDEX, &value);
98 ASSERT_RETURN_SET_ERROR(status == napi_ok, std::make_shared<InnerError>("napi_get_element value failed."));
99 ValueObject valueObject;
100 int32_t ret = JSUtils::Convert2Value(env, value, valueObject.value);
101 if (ret == napi_ok) {
102 valuesBucket.values_.insert_or_assign(std::move(keyStr), std::move(valueObject));
103 } else if (ret != napi_generic_failure) {
104 ASSERT_RETURN_SET_ERROR(false, std::make_shared<ParamError>("The value type of " + keyStr, "invalid."));
105 }
106 }
107 return OK;
108 }
109
ParseValuesBucket(napi_env env,napi_value arg,ValuesBucket & valuesBucket)110 int32_t TransactionContext::ParseValuesBucket(napi_env env, napi_value arg, ValuesBucket &valuesBucket)
111 {
112 bool isMap = false;
113 napi_status status = napi_is_map(env, arg, &isMap);
114 ASSERT_RETURN_SET_ERROR(
115 status == napi_ok, std::make_shared<InnerError>("call napi_is_map failed" + std::to_string(status)));
116 if (isMap) {
117 return ParseSendableValuesBucket(env, arg, valuesBucket);
118 }
119 napi_value keys = nullptr;
120 napi_get_all_property_names(env, arg, napi_key_own_only,
121 static_cast<napi_key_filter>(napi_key_enumerable | napi_key_skip_symbols), napi_key_numbers_to_strings, &keys);
122 uint32_t arrLen = 0;
123 status = napi_get_array_length(env, keys, &arrLen);
124 ASSERT_RETURN_SET_ERROR(status == napi_ok && arrLen > 0, std::make_shared<ParamError>("ValuesBucket is invalid"));
125
126 for (size_t i = 0; i < arrLen; ++i) {
127 napi_value key = nullptr;
128 status = napi_get_element(env, keys, i, &key);
129 ASSERT_RETURN_SET_ERROR(status == napi_ok, std::make_shared<ParamError>("ValuesBucket is invalid."));
130 std::string keyStr = JSUtils::Convert2String(env, key);
131 napi_value value = nullptr;
132 napi_get_property(env, arg, key, &value);
133 ValueObject valueObject;
134 int32_t ret = JSUtils::Convert2Value(env, value, valueObject.value);
135 if (ret == napi_ok) {
136 valuesBucket.values_.insert_or_assign(std::move(keyStr), std::move(valueObject));
137 } else if (ret != napi_generic_failure) {
138 ASSERT_RETURN_SET_ERROR(false, std::make_shared<ParamError>("The value type of " + keyStr, "invalid."));
139 }
140 }
141 return OK;
142 }
143
ParseValuesBuckets(napi_env env,napi_value arg,std::vector<ValuesBucket> & valuesBuckets)144 int32_t TransactionContext::ParseValuesBuckets(napi_env env, napi_value arg, std::vector<ValuesBucket> &valuesBuckets)
145 {
146 bool isArray = false;
147 auto status = napi_is_array(env, arg, &isArray);
148 ASSERT_RETURN_SET_ERROR(status == napi_ok && isArray, std::make_shared<ParamError>("ValuesBucket is invalid."));
149
150 uint32_t arrLen = 0;
151 status = napi_get_array_length(env, arg, &arrLen);
152 ASSERT_RETURN_SET_ERROR(status == napi_ok && arrLen > 0, std::make_shared<ParamError>("ValuesBucket is invalid."));
153
154 for (uint32_t i = 0; i < arrLen; ++i) {
155 napi_value obj = nullptr;
156 status = napi_get_element(env, arg, i, &obj);
157 ASSERT_RETURN_SET_ERROR(status == napi_ok, std::make_shared<InnerError>("napi_get_element failed."));
158 ValuesBucket valuesBucket;
159 ASSERT_RETURN_SET_ERROR(
160 ParseValuesBucket(env, obj, valuesBucket) == OK, std::make_shared<ParamError>("ValuesBucket is invalid."));
161 valuesBuckets.push_back(std::move(valuesBucket));
162 }
163 return OK;
164 }
165
ParseValuesBuckets(napi_env env,napi_value arg,ValuesBuckets & valuesBuckets)166 int32_t TransactionContext::ParseValuesBuckets(napi_env env, napi_value arg, ValuesBuckets &valuesBuckets)
167 {
168 bool isArray = false;
169 auto status = napi_is_array(env, arg, &isArray);
170 ASSERT_RETURN_SET_ERROR(status == napi_ok && isArray, std::make_shared<ParamError>("ValuesBucket is invalid."));
171
172 uint32_t arrLen = 0;
173 status = napi_get_array_length(env, arg, &arrLen);
174 ASSERT_RETURN_SET_ERROR(status == napi_ok && arrLen > 0, std::make_shared<ParamError>("ValuesBucket is invalid."));
175
176 for (uint32_t i = 0; i < arrLen; ++i) {
177 napi_value obj = nullptr;
178 status = napi_get_element(env, arg, i, &obj);
179 ASSERT_RETURN_SET_ERROR(status == napi_ok, std::make_shared<InnerError>("napi_get_element failed."));
180 ValuesBucket valuesBucket;
181 ASSERT_RETURN_SET_ERROR(
182 ParseValuesBucket(env, obj, valuesBucket) == OK, std::make_shared<ParamError>("ValuesBucket is invalid."));
183 valuesBuckets.Put(std::move(valuesBucket));
184 }
185 return OK;
186 }
187
ParseConflictResolution(const napi_env env,const napi_value arg,NativeRdb::ConflictResolution & conflictResolution)188 int32_t TransactionContext::ParseConflictResolution(
189 const napi_env env, const napi_value arg, NativeRdb::ConflictResolution &conflictResolution)
190 {
191 int32_t input = 0;
192 auto status = napi_get_value_int32(env, arg, &input);
193 int32_t min = static_cast<int32_t>(NativeRdb::ConflictResolution::ON_CONFLICT_NONE);
194 int32_t max = static_cast<int32_t>(NativeRdb::ConflictResolution::ON_CONFLICT_REPLACE);
195 bool checked = status == napi_ok && (input >= min) && (input <= max);
196 ASSERT_RETURN_SET_ERROR(checked, std::make_shared<ParamError>("conflictResolution", "a ConflictResolution."));
197 conflictResolution = static_cast<NativeRdb::ConflictResolution>(input);
198 return OK;
199 }
200
NewInstance(napi_env env,std::shared_ptr<NativeRdb::Transaction> transaction)201 napi_value TransactionProxy::NewInstance(napi_env env, std::shared_ptr<NativeRdb::Transaction> transaction)
202 {
203 napi_value cons = JSUtils::GetClass(env, "ohos.data.relationalStore", "Transaction");
204 if (cons == nullptr) {
205 LOG_ERROR("Constructor of Transaction is nullptr!");
206 return nullptr;
207 }
208 napi_value instance = nullptr;
209 auto status = napi_new_instance(env, cons, 0, nullptr, &instance);
210 if (status != napi_ok) {
211 LOG_ERROR("NewInstance napi_new_instance failed! code:%{public}d!", status);
212 return nullptr;
213 }
214
215 TransactionProxy *proxy = nullptr;
216 status = napi_unwrap(env, instance, reinterpret_cast<void **>(&proxy));
217 if (status != napi_ok || proxy == nullptr) {
218 LOG_ERROR("NewInstance native instance is nullptr! code:%{public}d!", status);
219 return nullptr;
220 }
221 proxy->SetInstance(std::move(transaction));
222 return instance;
223 }
224
Init(napi_env env,napi_value exports)225 void TransactionProxy::Init(napi_env env, napi_value exports)
226 {
227 auto lambda = []() -> std::vector<napi_property_descriptor> {
228 std::vector<napi_property_descriptor> properties = {
229 DECLARE_NAPI_FUNCTION("rollback", Rollback),
230 DECLARE_NAPI_FUNCTION("commit", Commit),
231 DECLARE_NAPI_FUNCTION_WITH_DATA("delete", Delete, ASYNC),
232 DECLARE_NAPI_FUNCTION_WITH_DATA("update", Update, ASYNC),
233 DECLARE_NAPI_FUNCTION_WITH_DATA("insert", Insert, ASYNC),
234 DECLARE_NAPI_FUNCTION_WITH_DATA("batchInsert", BatchInsert, ASYNC),
235 DECLARE_NAPI_FUNCTION_WITH_DATA(
236 "batchInsertWithConflictResolution", BatchInsertWithConflictResolution, ASYNC),
237 DECLARE_NAPI_FUNCTION_WITH_DATA("query", Query, ASYNC),
238 DECLARE_NAPI_FUNCTION_WITH_DATA("querySql", QuerySql, ASYNC),
239 DECLARE_NAPI_FUNCTION_WITH_DATA("execute", Execute, ASYNC),
240 };
241 AddSyncFunctions(properties);
242 return properties;
243 };
244 auto jsCtor = JSUtils::DefineClass(env, "ohos.data.relationalStore", "Transaction", lambda, Initialize);
245 NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, exports, "Transaction", jsCtor));
246
247 LOG_DEBUG("TransactionProxy::Init end.");
248 }
249
AddSyncFunctions(std::vector<napi_property_descriptor> & properties)250 void TransactionProxy::AddSyncFunctions(std::vector<napi_property_descriptor> &properties)
251 {
252 properties.push_back(DECLARE_NAPI_FUNCTION_WITH_DATA("deleteSync", Delete, SYNC));
253 properties.push_back(DECLARE_NAPI_FUNCTION_WITH_DATA("updateSync", Update, SYNC));
254 properties.push_back(DECLARE_NAPI_FUNCTION_WITH_DATA("insertSync", Insert, SYNC));
255 properties.push_back(DECLARE_NAPI_FUNCTION_WITH_DATA("batchInsertSync", BatchInsert, SYNC));
256 properties.push_back(DECLARE_NAPI_FUNCTION_WITH_DATA("batchInsertWithConflictResolutionSync",
257 BatchInsertWithConflictResolution, SYNC));
258 properties.push_back(DECLARE_NAPI_FUNCTION_WITH_DATA("querySync", Query, SYNC));
259 properties.push_back(DECLARE_NAPI_FUNCTION_WITH_DATA("querySqlSync", QuerySql, SYNC));
260 properties.push_back(DECLARE_NAPI_FUNCTION_WITH_DATA("executeSync", Execute, SYNC));
261 }
262
~TransactionProxy()263 TransactionProxy::~TransactionProxy()
264 {
265 }
266
TransactionProxy(std::shared_ptr<NativeRdb::Transaction> transaction)267 TransactionProxy::TransactionProxy(std::shared_ptr<NativeRdb::Transaction> transaction)
268 {
269 if (GetInstance() == transaction) {
270 return;
271 }
272 SetInstance(std::move(transaction));
273 }
274
Initialize(napi_env env,napi_callback_info info)275 napi_value TransactionProxy::Initialize(napi_env env, napi_callback_info info)
276 {
277 napi_value self = nullptr;
278 NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &self, nullptr));
279 auto *proxy = new (std::nothrow) TransactionProxy();
280 if (proxy == nullptr) {
281 LOG_ERROR("No memory, new TransactionProxy failed!");
282 return nullptr;
283 }
284 auto finalize = [](napi_env env, void *data, void *hint) {
285 auto tid = JSDFManager::GetInstance().GetFreedTid(data);
286 if (tid != 0) {
287 LOG_ERROR("(T:%{public}d) freed! data:0x%016" PRIXPTR, tid, uintptr_t(data) & LOWER_24_BITS_MASK);
288 }
289 if (data != hint) {
290 LOG_ERROR("Memory corrupted! data:0x%016" PRIXPTR "hint:0x%016" PRIXPTR, uintptr_t(data), uintptr_t(hint));
291 return;
292 }
293 TransactionProxy *proxy = reinterpret_cast<TransactionProxy *>(data);
294 proxy->SetInstance(nullptr);
295 delete proxy;
296 };
297 napi_status status = napi_wrap(env, self, proxy, finalize, proxy, nullptr);
298 if (status != napi_ok) {
299 LOG_ERROR("napi_wrap failed! code:%{public}d!", status);
300 finalize(env, proxy, proxy);
301 return nullptr;
302 }
303 JSDFManager::GetInstance().AddNewInfo(proxy);
304 return self;
305 }
306
307 struct CommitContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::CommitContext308 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
309 {
310 GetInstance(self);
311 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
312 return OK;
313 }
314 };
315 /*
316 * [JS API Prototype]
317 * [Promise]
318 * commit(): Promise<void>;
319 */
Commit(napi_env env,napi_callback_info info)320 napi_value TransactionProxy::Commit(napi_env env, napi_callback_info info)
321 {
322 auto context = std::make_shared<CommitContext>();
323 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
324 context->Parse(env, argc, argv, self);
325 };
326 auto exec = [context]() -> int {
327 CHECK_RETURN_ERR(context->transaction_ != nullptr);
328 return context->StealTransaction()->Commit();
329 };
330 auto output = [context](napi_env env, napi_value &result) {
331 napi_status status = napi_get_undefined(env, &result);
332 CHECK_RETURN_SET_E(status == napi_ok, std::make_shared<InnerError>(E_ERROR));
333 };
334 context->SetAction(env, info, input, exec, output);
335
336 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
337 return ASYNC_CALL(env, context);
338 }
339
340 struct RollbackContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::RollbackContext341 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
342 {
343 GetInstance(self);
344 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
345 return OK;
346 }
347 };
348
349 /*
350 * [JS API Prototype]
351 * [Promise]
352 * rollback(): Promise<void>;
353 */
Rollback(napi_env env,napi_callback_info info)354 napi_value TransactionProxy::Rollback(napi_env env, napi_callback_info info)
355 {
356 auto context = std::make_shared<RollbackContext>();
357 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
358 context->Parse(env, argc, argv, self);
359 };
360 auto exec = [context]() -> int {
361 CHECK_RETURN_ERR(context->transaction_ != nullptr);
362 return context->StealTransaction()->Rollback();
363 };
364 auto output = [context](napi_env env, napi_value &result) {
365 napi_status status = napi_get_undefined(env, &result);
366 CHECK_RETURN_SET_E(status == napi_ok, std::make_shared<InnerError>(E_ERROR));
367 };
368 context->SetAction(env, info, input, exec, output);
369
370 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
371 return ASYNC_CALL(env, context);
372 }
373
374 struct DeleteContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::DeleteContext375 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
376 {
377 ASSERT_RETURN_SET_ERROR(argc == 1, std::make_shared<ParamNumError>("1"));
378 GetInstance(self);
379 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
380 CHECK_RETURN_ERR(ParseRdbPredicatesProxy(env, argv[0], rdbPredicates) == OK);
381 return OK;
382 }
383 std::shared_ptr<RdbPredicates> rdbPredicates = nullptr;
384
385 int64_t deleteRows = -1;
386 };
387
388 /*
389 * [JS API Prototype]
390 * [Promise]
391 * delete(predicates: RdbPredicates): Promise<number>;
392 */
Delete(napi_env env,napi_callback_info info)393 napi_value TransactionProxy::Delete(napi_env env, napi_callback_info info)
394 {
395 auto context = std::make_shared<DeleteContext>();
396 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
397 context->Parse(env, argc, argv, self);
398 };
399 auto exec = [context]() -> int {
400 CHECK_RETURN_ERR(context->transaction_ != nullptr && context->rdbPredicates != nullptr);
401 auto [code, deleteRows] = context->StealTransaction()->Delete(*(context->rdbPredicates));
402 context->deleteRows = deleteRows;
403 return code;
404 };
405 auto output = [context](napi_env env, napi_value &result) {
406 napi_status status = napi_create_int64(env, context->deleteRows, &result);
407 CHECK_RETURN_SET_E(status == napi_ok, std::make_shared<InnerError>(E_ERROR));
408 };
409 context->SetAction(env, info, input, exec, output);
410
411 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
412 return ASYNC_CALL(env, context);
413 }
414
415 struct UpdateContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::UpdateContext416 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
417 {
418 ASSERT_RETURN_SET_ERROR(argc == 2 || argc == 3, std::make_shared<ParamNumError>("2 to 3"));
419 GetInstance(self);
420 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
421 CHECK_RETURN_ERR(ParseValuesBucket(env, argv[0], valuesBucket) == OK);
422 CHECK_RETURN_ERR(ParseRdbPredicatesProxy(env, argv[1], rdbPredicates) == OK);
423 // 'argv[2]' is an optional parameter
424 if (argc > 2 && !JSUtils::IsNull(env, argv[2])) {
425 // 'argv[2]' represents a ConflictResolution parameter
426 CHECK_RETURN_ERR(ParseConflictResolution(env, argv[2], conflictResolution));
427 }
428 return OK;
429 }
430 ValuesBucket valuesBucket;
431 std::shared_ptr<RdbPredicates> rdbPredicates = nullptr;
432 NativeRdb::ConflictResolution conflictResolution = ConflictResolution::ON_CONFLICT_NONE;
433
434 int64_t updateRows = -1;
435 };
436
437 /*
438 * [JS API Prototype]
439 * [Promise]
440 * update(values: ValuesBucket, predicates: RdbPredicates, conflict?: ConflictResolution): Promise<number>;
441 */
Update(napi_env env,napi_callback_info info)442 napi_value TransactionProxy::Update(napi_env env, napi_callback_info info)
443 {
444 auto context = std::make_shared<UpdateContext>();
445 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
446 context->Parse(env, argc, argv, self);
447 };
448 auto exec = [context]() -> int {
449 CHECK_RETURN_ERR(context->transaction_ != nullptr && context->rdbPredicates != nullptr);
450 auto [code, updateRows] = context->StealTransaction()->Update(
451 context->valuesBucket, *context->rdbPredicates, context->conflictResolution);
452 context->updateRows = updateRows;
453 return code;
454 };
455 auto output = [context](napi_env env, napi_value &result) {
456 napi_status status = napi_create_int64(env, context->updateRows, &result);
457 CHECK_RETURN_SET_E(status == napi_ok, std::make_shared<InnerError>(E_ERROR));
458 };
459 context->SetAction(env, info, input, exec, output);
460
461 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
462 return ASYNC_CALL(env, context);
463 }
464
465 struct InsertContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::InsertContext466 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
467 {
468 ASSERT_RETURN_SET_ERROR(argc == 2 || argc == 3, std::make_shared<ParamNumError>("2 to 3"));
469 GetInstance(self);
470 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
471 CHECK_RETURN_ERR(JSUtils::Convert2Value(env, argv[0], tableName) == OK);
472 CHECK_RETURN_ERR(ParseValuesBucket(env, argv[1], valuesBucket) == OK);
473 // 'argv[2]' is an optional parameter
474 if (argc > 2 && !JSUtils::IsNull(env, argv[2])) {
475 // 'argv[2]' represents a ConflictResolution parameter
476 CHECK_RETURN_ERR(ParseConflictResolution(env, argv[2], conflictResolution));
477 }
478 return OK;
479 }
480 std::string tableName;
481 ValuesBucket valuesBucket;
482 NativeRdb::ConflictResolution conflictResolution = ConflictResolution::ON_CONFLICT_NONE;
483
484 int64_t insertRows = -1;
485 };
486
487 /*
488 * [JS API Prototype]
489 * [Promise]
490 * insert(table: string, values: ValuesBucket, conflict?: ConflictResolution): Promise<number>;
491 */
Insert(napi_env env,napi_callback_info info)492 napi_value TransactionProxy::Insert(napi_env env, napi_callback_info info)
493 {
494 auto context = std::make_shared<InsertContext>();
495 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
496 context->Parse(env, argc, argv, self);
497 };
498 auto exec = [context]() -> int {
499 CHECK_RETURN_ERR(context->transaction_ != nullptr);
500 auto [code, insertRows] = context->StealTransaction()->Insert(
501 context->tableName, context->valuesBucket, context->conflictResolution);
502 context->insertRows = insertRows;
503 return code;
504 };
505 auto output = [context](napi_env env, napi_value &result) {
506 napi_status status = napi_create_int64(env, context->insertRows, &result);
507 CHECK_RETURN_SET_E(status == napi_ok, std::make_shared<InnerError>(E_ERROR));
508 };
509 context->SetAction(env, info, input, exec, output);
510
511 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
512 return ASYNC_CALL(env, context);
513 }
514
515 struct BatchInsertContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::BatchInsertContext516 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
517 {
518 ASSERT_RETURN_SET_ERROR(argc == 2, std::make_shared<ParamNumError>("2"));
519 GetInstance(self);
520 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
521 ASSERT_RETURN_SET_ERROR(
522 JSUtils::Convert2Value(env, argv[0], tableName) == OK, std::make_shared<ParamError>("table", "a string."));
523 CHECK_RETURN_ERR(ParseValuesBuckets(env, argv[1], valuesBuckets) == OK);
524 ASSERT_RETURN_SET_ERROR(!JSUtils::HasDuplicateAssets(valuesBuckets),
525 std::make_shared<ParamError>("Duplicate assets are not allowed"));
526 return OK;
527 }
528 std::string tableName;
529 std::vector<ValuesBucket> valuesBuckets;
530
531 int64_t insertRows = -1;
532 };
533
534 /*
535 * [JS API Prototype]
536 * [Promise]
537 * batchInsert(table: string, values: Array<ValuesBucket>): Promise<number>;
538 */
BatchInsert(napi_env env,napi_callback_info info)539 napi_value TransactionProxy::BatchInsert(napi_env env, napi_callback_info info)
540 {
541 auto context = std::make_shared<BatchInsertContext>();
542 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
543 context->Parse(env, argc, argv, self);
544 };
545 auto exec = [context]() -> int {
546 CHECK_RETURN_ERR(context->transaction_ != nullptr);
547 auto [code, insertRows] = context->StealTransaction()->BatchInsert(context->tableName, context->valuesBuckets);
548 context->insertRows = insertRows;
549 return code;
550 };
551 auto output = [context](napi_env env, napi_value &result) {
552 napi_status status = napi_create_int64(env, context->insertRows, &result);
553 CHECK_RETURN_SET_E(status == napi_ok, std::make_shared<InnerError>(E_ERROR));
554 };
555 context->SetAction(env, info, input, exec, output);
556
557 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
558 return ASYNC_CALL(env, context);
559 }
560
561 struct BatchInsertWithConflictResolutionContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::BatchInsertWithConflictResolutionContext562 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
563 {
564 ASSERT_RETURN_SET_ERROR(argc == 3, std::make_shared<ParamNumError>("3"));
565 GetInstance(self);
566 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
567 ASSERT_RETURN_SET_ERROR(
568 JSUtils::Convert2Value(env, argv[0], tableName) == OK, std::make_shared<ParamError>("table", "a string."));
569 CHECK_RETURN_ERR(ParseValuesBuckets(env, argv[1], valuesBuckets) == OK);
570 ASSERT_RETURN_SET_ERROR(!JSUtils::HasDuplicateAssets(valuesBuckets),
571 std::make_shared<ParamError>("Duplicate assets are not allowed"));
572 // 'argv[2]' represents a ConflictResolution
573 ASSERT_RETURN_SET_ERROR(!JSUtils::IsNull(env, argv[2]), std::make_shared<ParamError>("conflict", "not null"));
574 // 'argv[2]' represents a ConflictResolution
575 CHECK_RETURN_ERR(ParseConflictResolution(env, argv[2], conflictResolution) == OK);
576 return OK;
577 }
578 std::string tableName;
579 ValuesBuckets valuesBuckets;
580 ConflictResolution conflictResolution;
581
582 int64_t insertRows = -1;
583 };
584
585 /*
586 * [JS API Prototype]
587 * [Promise]
588 * batchInsertWithConflictResolution(table: string, values: Array<ValuesBucket>, conflict: ConflictResolution):
589 * Promise<number>;
590 */
BatchInsertWithConflictResolution(napi_env env,napi_callback_info info)591 napi_value TransactionProxy::BatchInsertWithConflictResolution(napi_env env, napi_callback_info info)
592 {
593 auto context = std::make_shared<BatchInsertWithConflictResolutionContext>();
594 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
595 context->Parse(env, argc, argv, self);
596 };
597 auto exec = [context]() -> int {
598 CHECK_RETURN_ERR(context->transaction_ != nullptr);
599 auto [code, insertRows] = context->StealTransaction()->BatchInsertWithConflictResolution(
600 context->tableName, context->valuesBuckets, context->conflictResolution);
601 context->insertRows = insertRows;
602 return code;
603 };
604 auto output = [context](napi_env env, napi_value &result) {
605 napi_status status = napi_create_int64(env, context->insertRows, &result);
606 CHECK_RETURN_SET_E(status == napi_ok, std::make_shared<InnerError>(E_ERROR));
607 };
608 context->SetAction(env, info, input, exec, output);
609
610 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
611 return ASYNC_CALL(env, context);
612 }
613
614 struct QueryContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::QueryContext615 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
616 {
617 ASSERT_RETURN_SET_ERROR(argc == 1 || argc == 2, std::make_shared<ParamNumError>("1 to 2"));
618 GetInstance(self);
619 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
620 CHECK_RETURN_ERR(ParseRdbPredicatesProxy(env, argv[0], rdbPredicates) == OK);
621 if (argc > 1 && !JSUtils::IsNull(env, argv[1])) {
622 ASSERT_RETURN_SET_ERROR(JSUtils::Convert2Value(env, argv[1], columns) == OK,
623 std::make_shared<ParamError>("columns", "a Array<string>."));
624 }
625 return OK;
626 }
627 std::shared_ptr<RdbPredicates> rdbPredicates = nullptr;
628 std::vector<std::string> columns;
629
630 std::shared_ptr<ResultSet> resultSet;
631 };
632
633 /*
634 * [JS API Prototype]
635 * [Promise]
636 * query(predicates: RdbPredicates, columns?: Array<string>): Promise<ResultSet>;
637 */
Query(napi_env env,napi_callback_info info)638 napi_value TransactionProxy::Query(napi_env env, napi_callback_info info)
639 {
640 auto context = std::make_shared<QueryContext>();
641 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
642 context->Parse(env, argc, argv, self);
643 };
644 auto exec = [context]() -> int {
645 CHECK_RETURN_ERR(context->transaction_ != nullptr && context->rdbPredicates != nullptr);
646 context->resultSet = context->StealTransaction()->QueryByStep(*(context->rdbPredicates), context->columns);
647 return (context->resultSet != nullptr) ? E_OK : E_ALREADY_CLOSED;
648 };
649 auto output = [context](napi_env env, napi_value &result) {
650 result = ResultSetProxy::NewInstance(env, context->resultSet);
651 CHECK_RETURN_SET_E(result != nullptr, std::make_shared<InnerError>(E_ERROR));
652 };
653 context->SetAction(env, info, input, exec, output);
654
655 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
656 return ASYNC_CALL(env, context);
657 }
658
659 struct QuerySqlContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::QuerySqlContext660 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
661 {
662 ASSERT_RETURN_SET_ERROR(argc == 1 || argc == 2, std::make_shared<ParamNumError>("1 to 2"));
663 GetInstance(self);
664 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
665 ASSERT_RETURN_SET_ERROR(
666 JSUtils::Convert2Value(env, argv[0], sql) == OK, std::make_shared<ParamError>("sql", "a string."));
667 if (argc > 1 && !JSUtils::IsNull(env, argv[1])) {
668 ASSERT_RETURN_SET_ERROR(JSUtils::Convert2Value(env, argv[1], bindArgs) == OK,
669 std::make_shared<ParamError>("bindArgs", "a Array<ValueType>."));
670 }
671 return OK;
672 }
673 std::string sql;
674 std::vector<ValueObject> bindArgs;
675
676 std::shared_ptr<ResultSet> resultSet;
677 };
678
679 /*
680 * [JS API Prototype]
681 * [Promise]
682 * querySql(sql: string, bindArgs?: Array<ValueType>): Promise<ResultSet>;
683 */
QuerySql(napi_env env,napi_callback_info info)684 napi_value TransactionProxy::QuerySql(napi_env env, napi_callback_info info)
685 {
686 auto context = std::make_shared<QuerySqlContext>();
687 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
688 context->Parse(env, argc, argv, self);
689 };
690 auto exec = [context]() -> int {
691 CHECK_RETURN_ERR(context->transaction_ != nullptr);
692 context->resultSet = context->StealTransaction()->QueryByStep(context->sql, context->bindArgs);
693 return (context->resultSet != nullptr) ? E_OK : E_ALREADY_CLOSED;
694 };
695 auto output = [context](napi_env env, napi_value &result) {
696 result = ResultSetProxy::NewInstance(env, context->resultSet);
697 CHECK_RETURN_SET_E(result != nullptr, std::make_shared<InnerError>(E_ERROR));
698 };
699 context->SetAction(env, info, input, exec, output);
700
701 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
702 return ASYNC_CALL(env, context);
703 }
704
705 struct ExecuteContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::ExecuteContext706 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
707 {
708 ASSERT_RETURN_SET_ERROR(argc == 1 || argc == 2, std::make_shared<ParamNumError>("1 to 2"));
709 GetInstance(self);
710 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
711 CHECK_RETURN_ERR(JSUtils::Convert2Value(env, argv[0], sql) == OK);
712 if (argc > 1 && !JSUtils::IsNull(env, argv[1])) {
713 CHECK_RETURN_ERR(JSUtils::Convert2Value(env, argv[1], bindArgs) == OK);
714 }
715 return OK;
716 }
717 std::string sql;
718 std::vector<ValueObject> bindArgs;
719
720 ValueObject output;
721 };
722
723 /*
724 * [JS API Prototype]
725 * [Promise]
726 * execute(sql: string, args?: Array<ValueType>): Promise<ValueType>;
727 */
Execute(napi_env env,napi_callback_info info)728 napi_value TransactionProxy::Execute(napi_env env, napi_callback_info info)
729 {
730 auto context = std::make_shared<ExecuteContext>();
731 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
732 context->Parse(env, argc, argv, self);
733 };
734 auto exec = [context]() -> int {
735 CHECK_RETURN_ERR(context->transaction_ != nullptr);
736 auto status = E_ERROR;
737 std::tie(status, context->output) = context->StealTransaction()->Execute(context->sql, context->bindArgs);
738 return status;
739 };
740 auto output = [context](napi_env env, napi_value &result) {
741 result = JSUtils::Convert2JSValue(env, context->output);
742 CHECK_RETURN_SET_E(result != nullptr, std::make_shared<InnerError>(E_ERROR));
743 };
744 context->SetAction(env, info, input, exec, output);
745
746 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
747 return ASYNC_CALL(env, context);
748 }
749 } // namespace OHOS::RelationalStoreJsKit