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 if (data == nullptr) {
286 LOG_ERROR("data is nullptr.");
287 return;
288 }
289 auto tid = JSDFManager::GetInstance().GetFreedTid(data);
290 if (tid != 0) {
291 LOG_ERROR("(T:%{public}d) freed! data:0x%016" PRIXPTR, tid, uintptr_t(data) & LOWER_24_BITS_MASK);
292 }
293 if (data != hint) {
294 LOG_ERROR("Memory corrupted! data:0x%016" PRIXPTR "hint:0x%016" PRIXPTR,
295 uintptr_t(data) & LOWER_24_BITS_MASK, uintptr_t(hint) & LOWER_24_BITS_MASK);
296 return;
297 }
298 TransactionProxy *proxy = reinterpret_cast<TransactionProxy *>(data);
299 proxy->SetInstance(nullptr);
300 delete proxy;
301 };
302 napi_status status = napi_wrap(env, self, proxy, finalize, proxy, nullptr);
303 if (status != napi_ok) {
304 LOG_ERROR("napi_wrap failed! code:%{public}d!", status);
305 finalize(env, proxy, proxy);
306 return nullptr;
307 }
308 JSDFManager::GetInstance().AddNewInfo(proxy);
309 return self;
310 }
311
312 struct CommitContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::CommitContext313 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
314 {
315 GetInstance(self);
316 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
317 return OK;
318 }
319 };
320 /*
321 * [JS API Prototype]
322 * [Promise]
323 * commit(): Promise<void>;
324 */
Commit(napi_env env,napi_callback_info info)325 napi_value TransactionProxy::Commit(napi_env env, napi_callback_info info)
326 {
327 auto context = std::make_shared<CommitContext>();
328 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
329 context->Parse(env, argc, argv, self);
330 };
331 auto exec = [context]() -> int {
332 CHECK_RETURN_ERR(context->transaction_ != nullptr);
333 return context->StealTransaction()->Commit();
334 };
335 auto output = [context](napi_env env, napi_value &result) {
336 napi_status status = napi_get_undefined(env, &result);
337 CHECK_RETURN_SET_E(status == napi_ok, std::make_shared<InnerError>(E_ERROR));
338 };
339 context->SetAction(env, info, input, exec, output);
340
341 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
342 return ASYNC_CALL(env, context);
343 }
344
345 struct RollbackContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::RollbackContext346 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
347 {
348 GetInstance(self);
349 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
350 return OK;
351 }
352 };
353
354 /*
355 * [JS API Prototype]
356 * [Promise]
357 * rollback(): Promise<void>;
358 */
Rollback(napi_env env,napi_callback_info info)359 napi_value TransactionProxy::Rollback(napi_env env, napi_callback_info info)
360 {
361 auto context = std::make_shared<RollbackContext>();
362 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
363 context->Parse(env, argc, argv, self);
364 };
365 auto exec = [context]() -> int {
366 CHECK_RETURN_ERR(context->transaction_ != nullptr);
367 return context->StealTransaction()->Rollback();
368 };
369 auto output = [context](napi_env env, napi_value &result) {
370 napi_status status = napi_get_undefined(env, &result);
371 CHECK_RETURN_SET_E(status == napi_ok, std::make_shared<InnerError>(E_ERROR));
372 };
373 context->SetAction(env, info, input, exec, output);
374
375 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
376 return ASYNC_CALL(env, context);
377 }
378
379 struct DeleteContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::DeleteContext380 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
381 {
382 ASSERT_RETURN_SET_ERROR(argc == 1, std::make_shared<ParamNumError>("1"));
383 GetInstance(self);
384 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
385 CHECK_RETURN_ERR(ParseRdbPredicatesProxy(env, argv[0], rdbPredicates) == OK);
386 return OK;
387 }
388 std::shared_ptr<RdbPredicates> rdbPredicates = nullptr;
389
390 int64_t deleteRows = -1;
391 };
392
393 /*
394 * [JS API Prototype]
395 * [Promise]
396 * delete(predicates: RdbPredicates): Promise<number>;
397 */
Delete(napi_env env,napi_callback_info info)398 napi_value TransactionProxy::Delete(napi_env env, napi_callback_info info)
399 {
400 auto context = std::make_shared<DeleteContext>();
401 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
402 context->Parse(env, argc, argv, self);
403 };
404 auto exec = [context]() -> int {
405 CHECK_RETURN_ERR(context->transaction_ != nullptr && context->rdbPredicates != nullptr);
406 auto [code, deleteRows] = context->StealTransaction()->Delete(*(context->rdbPredicates));
407 context->deleteRows = deleteRows;
408 return code;
409 };
410 auto output = [context](napi_env env, napi_value &result) {
411 napi_status status = napi_create_int64(env, context->deleteRows, &result);
412 CHECK_RETURN_SET_E(status == napi_ok, std::make_shared<InnerError>(E_ERROR));
413 };
414 context->SetAction(env, info, input, exec, output);
415
416 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
417 return ASYNC_CALL(env, context);
418 }
419
420 struct UpdateContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::UpdateContext421 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
422 {
423 ASSERT_RETURN_SET_ERROR(argc == 2 || argc == 3, std::make_shared<ParamNumError>("2 to 3"));
424 GetInstance(self);
425 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
426 CHECK_RETURN_ERR(ParseValuesBucket(env, argv[0], valuesBucket) == OK);
427 CHECK_RETURN_ERR(ParseRdbPredicatesProxy(env, argv[1], rdbPredicates) == OK);
428 // 'argv[2]' is an optional parameter
429 if (argc > 2 && !JSUtils::IsNull(env, argv[2])) {
430 // 'argv[2]' represents a ConflictResolution parameter
431 CHECK_RETURN_ERR(ParseConflictResolution(env, argv[2], conflictResolution));
432 }
433 return OK;
434 }
435 ValuesBucket valuesBucket;
436 std::shared_ptr<RdbPredicates> rdbPredicates = nullptr;
437 NativeRdb::ConflictResolution conflictResolution = ConflictResolution::ON_CONFLICT_NONE;
438
439 int64_t updateRows = -1;
440 };
441
442 /*
443 * [JS API Prototype]
444 * [Promise]
445 * update(values: ValuesBucket, predicates: RdbPredicates, conflict?: ConflictResolution): Promise<number>;
446 */
Update(napi_env env,napi_callback_info info)447 napi_value TransactionProxy::Update(napi_env env, napi_callback_info info)
448 {
449 auto context = std::make_shared<UpdateContext>();
450 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
451 context->Parse(env, argc, argv, self);
452 };
453 auto exec = [context]() -> int {
454 CHECK_RETURN_ERR(context->transaction_ != nullptr && context->rdbPredicates != nullptr);
455 auto [code, updateRows] = context->StealTransaction()->Update(
456 context->valuesBucket, *context->rdbPredicates, context->conflictResolution);
457 context->updateRows = updateRows;
458 return code;
459 };
460 auto output = [context](napi_env env, napi_value &result) {
461 napi_status status = napi_create_int64(env, context->updateRows, &result);
462 CHECK_RETURN_SET_E(status == napi_ok, std::make_shared<InnerError>(E_ERROR));
463 };
464 context->SetAction(env, info, input, exec, output);
465
466 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
467 return ASYNC_CALL(env, context);
468 }
469
470 struct InsertContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::InsertContext471 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
472 {
473 ASSERT_RETURN_SET_ERROR(argc == 2 || argc == 3, std::make_shared<ParamNumError>("2 to 3"));
474 GetInstance(self);
475 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
476 CHECK_RETURN_ERR(JSUtils::Convert2Value(env, argv[0], tableName) == OK);
477 CHECK_RETURN_ERR(ParseValuesBucket(env, argv[1], valuesBucket) == OK);
478 // 'argv[2]' is an optional parameter
479 if (argc > 2 && !JSUtils::IsNull(env, argv[2])) {
480 // 'argv[2]' represents a ConflictResolution parameter
481 CHECK_RETURN_ERR(ParseConflictResolution(env, argv[2], conflictResolution));
482 }
483 return OK;
484 }
485 std::string tableName;
486 ValuesBucket valuesBucket;
487 NativeRdb::ConflictResolution conflictResolution = ConflictResolution::ON_CONFLICT_NONE;
488
489 int64_t insertRows = -1;
490 };
491
492 /*
493 * [JS API Prototype]
494 * [Promise]
495 * insert(table: string, values: ValuesBucket, conflict?: ConflictResolution): Promise<number>;
496 */
Insert(napi_env env,napi_callback_info info)497 napi_value TransactionProxy::Insert(napi_env env, napi_callback_info info)
498 {
499 auto context = std::make_shared<InsertContext>();
500 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
501 context->Parse(env, argc, argv, self);
502 };
503 auto exec = [context]() -> int {
504 CHECK_RETURN_ERR(context->transaction_ != nullptr);
505 auto [code, insertRows] = context->StealTransaction()->Insert(
506 context->tableName, context->valuesBucket, context->conflictResolution);
507 context->insertRows = insertRows;
508 return code;
509 };
510 auto output = [context](napi_env env, napi_value &result) {
511 napi_status status = napi_create_int64(env, context->insertRows, &result);
512 CHECK_RETURN_SET_E(status == napi_ok, std::make_shared<InnerError>(E_ERROR));
513 };
514 context->SetAction(env, info, input, exec, output);
515
516 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
517 return ASYNC_CALL(env, context);
518 }
519
520 struct BatchInsertContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::BatchInsertContext521 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
522 {
523 ASSERT_RETURN_SET_ERROR(argc == 2, std::make_shared<ParamNumError>("2"));
524 GetInstance(self);
525 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
526 ASSERT_RETURN_SET_ERROR(
527 JSUtils::Convert2Value(env, argv[0], tableName) == OK, std::make_shared<ParamError>("table", "a string."));
528 CHECK_RETURN_ERR(ParseValuesBuckets(env, argv[1], valuesBuckets) == OK);
529 ASSERT_RETURN_SET_ERROR(!JSUtils::HasDuplicateAssets(valuesBuckets),
530 std::make_shared<ParamError>("Duplicate assets are not allowed"));
531 return OK;
532 }
533 std::string tableName;
534 std::vector<ValuesBucket> valuesBuckets;
535
536 int64_t insertRows = -1;
537 };
538
539 /*
540 * [JS API Prototype]
541 * [Promise]
542 * batchInsert(table: string, values: Array<ValuesBucket>): Promise<number>;
543 */
BatchInsert(napi_env env,napi_callback_info info)544 napi_value TransactionProxy::BatchInsert(napi_env env, napi_callback_info info)
545 {
546 auto context = std::make_shared<BatchInsertContext>();
547 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
548 context->Parse(env, argc, argv, self);
549 };
550 auto exec = [context]() -> int {
551 CHECK_RETURN_ERR(context->transaction_ != nullptr);
552 auto [code, insertRows] = context->StealTransaction()->BatchInsert(context->tableName, context->valuesBuckets);
553 context->insertRows = insertRows;
554 return code;
555 };
556 auto output = [context](napi_env env, napi_value &result) {
557 napi_status status = napi_create_int64(env, context->insertRows, &result);
558 CHECK_RETURN_SET_E(status == napi_ok, std::make_shared<InnerError>(E_ERROR));
559 };
560 context->SetAction(env, info, input, exec, output);
561
562 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
563 return ASYNC_CALL(env, context);
564 }
565
566 struct BatchInsertWithConflictResolutionContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::BatchInsertWithConflictResolutionContext567 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
568 {
569 ASSERT_RETURN_SET_ERROR(argc == 3, std::make_shared<ParamNumError>("3"));
570 GetInstance(self);
571 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
572 ASSERT_RETURN_SET_ERROR(
573 JSUtils::Convert2Value(env, argv[0], tableName) == OK, std::make_shared<ParamError>("table", "a string."));
574 CHECK_RETURN_ERR(ParseValuesBuckets(env, argv[1], valuesBuckets) == OK);
575 ASSERT_RETURN_SET_ERROR(!JSUtils::HasDuplicateAssets(valuesBuckets),
576 std::make_shared<ParamError>("Duplicate assets are not allowed"));
577 // 'argv[2]' represents a ConflictResolution
578 ASSERT_RETURN_SET_ERROR(!JSUtils::IsNull(env, argv[2]), std::make_shared<ParamError>("conflict", "not null"));
579 // 'argv[2]' represents a ConflictResolution
580 CHECK_RETURN_ERR(ParseConflictResolution(env, argv[2], conflictResolution) == OK);
581 return OK;
582 }
583 std::string tableName;
584 ValuesBuckets valuesBuckets;
585 ConflictResolution conflictResolution;
586
587 int64_t insertRows = -1;
588 };
589
590 /*
591 * [JS API Prototype]
592 * [Promise]
593 * batchInsertWithConflictResolution(table: string, values: Array<ValuesBucket>, conflict: ConflictResolution):
594 * Promise<number>;
595 */
BatchInsertWithConflictResolution(napi_env env,napi_callback_info info)596 napi_value TransactionProxy::BatchInsertWithConflictResolution(napi_env env, napi_callback_info info)
597 {
598 auto context = std::make_shared<BatchInsertWithConflictResolutionContext>();
599 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
600 context->Parse(env, argc, argv, self);
601 };
602 auto exec = [context]() -> int {
603 CHECK_RETURN_ERR(context->transaction_ != nullptr);
604 auto [code, insertRows] = context->StealTransaction()->BatchInsert(
605 context->tableName, context->valuesBuckets, context->conflictResolution);
606 context->insertRows = insertRows;
607 return code;
608 };
609 auto output = [context](napi_env env, napi_value &result) {
610 napi_status status = napi_create_int64(env, context->insertRows, &result);
611 CHECK_RETURN_SET_E(status == napi_ok, std::make_shared<InnerError>(E_ERROR));
612 };
613 context->SetAction(env, info, input, exec, output);
614
615 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
616 return ASYNC_CALL(env, context);
617 }
618
619 struct QueryContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::QueryContext620 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
621 {
622 ASSERT_RETURN_SET_ERROR(argc == 1 || argc == 2, std::make_shared<ParamNumError>("1 to 2"));
623 GetInstance(self);
624 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
625 CHECK_RETURN_ERR(ParseRdbPredicatesProxy(env, argv[0], rdbPredicates) == OK);
626 if (argc > 1 && !JSUtils::IsNull(env, argv[1])) {
627 ASSERT_RETURN_SET_ERROR(JSUtils::Convert2Value(env, argv[1], columns) == OK,
628 std::make_shared<ParamError>("columns", "a Array<string>."));
629 }
630 return OK;
631 }
632 std::shared_ptr<RdbPredicates> rdbPredicates = nullptr;
633 std::vector<std::string> columns;
634
635 std::shared_ptr<ResultSet> resultSet;
636 };
637
638 /*
639 * [JS API Prototype]
640 * [Promise]
641 * query(predicates: RdbPredicates, columns?: Array<string>): Promise<ResultSet>;
642 */
Query(napi_env env,napi_callback_info info)643 napi_value TransactionProxy::Query(napi_env env, napi_callback_info info)
644 {
645 auto context = std::make_shared<QueryContext>();
646 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
647 context->Parse(env, argc, argv, self);
648 };
649 auto exec = [context]() -> int {
650 CHECK_RETURN_ERR(context->transaction_ != nullptr && context->rdbPredicates != nullptr);
651 context->resultSet = context->StealTransaction()->QueryByStep(*(context->rdbPredicates), context->columns);
652 return (context->resultSet != nullptr) ? E_OK : E_ALREADY_CLOSED;
653 };
654 auto output = [context](napi_env env, napi_value &result) {
655 result = ResultSetProxy::NewInstance(env, std::move(context->resultSet));
656 CHECK_RETURN_SET_E(result != nullptr, std::make_shared<InnerError>(E_ERROR));
657 };
658 context->SetAction(env, info, input, exec, output);
659
660 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
661 return ASYNC_CALL(env, context);
662 }
663
664 struct QuerySqlContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::QuerySqlContext665 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
666 {
667 ASSERT_RETURN_SET_ERROR(argc == 1 || argc == 2, std::make_shared<ParamNumError>("1 to 2"));
668 GetInstance(self);
669 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
670 ASSERT_RETURN_SET_ERROR(
671 JSUtils::Convert2Value(env, argv[0], sql) == OK, std::make_shared<ParamError>("sql", "a string."));
672 if (argc > 1 && !JSUtils::IsNull(env, argv[1])) {
673 ASSERT_RETURN_SET_ERROR(JSUtils::Convert2Value(env, argv[1], bindArgs) == OK,
674 std::make_shared<ParamError>("bindArgs", "a Array<ValueType>."));
675 }
676 return OK;
677 }
678 std::string sql;
679 std::vector<ValueObject> bindArgs;
680
681 std::shared_ptr<ResultSet> resultSet;
682 };
683
684 /*
685 * [JS API Prototype]
686 * [Promise]
687 * querySql(sql: string, bindArgs?: Array<ValueType>): Promise<ResultSet>;
688 */
QuerySql(napi_env env,napi_callback_info info)689 napi_value TransactionProxy::QuerySql(napi_env env, napi_callback_info info)
690 {
691 auto context = std::make_shared<QuerySqlContext>();
692 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
693 context->Parse(env, argc, argv, self);
694 };
695 auto exec = [context]() -> int {
696 CHECK_RETURN_ERR(context->transaction_ != nullptr);
697 context->resultSet = context->StealTransaction()->QueryByStep(context->sql, context->bindArgs);
698 return (context->resultSet != nullptr) ? E_OK : E_ALREADY_CLOSED;
699 };
700 auto output = [context](napi_env env, napi_value &result) {
701 result = ResultSetProxy::NewInstance(env, std::move(context->resultSet));
702 CHECK_RETURN_SET_E(result != nullptr, std::make_shared<InnerError>(E_ERROR));
703 };
704 context->SetAction(env, info, input, exec, output);
705
706 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
707 return ASYNC_CALL(env, context);
708 }
709
710 struct ExecuteContext : public TransactionContext {
ParseOHOS::RelationalStoreJsKit::ExecuteContext711 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
712 {
713 ASSERT_RETURN_SET_ERROR(argc == 1 || argc == 2, std::make_shared<ParamNumError>("1 to 2"));
714 GetInstance(self);
715 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
716 CHECK_RETURN_ERR(JSUtils::Convert2Value(env, argv[0], sql) == OK);
717 if (argc > 1 && !JSUtils::IsNull(env, argv[1])) {
718 CHECK_RETURN_ERR(JSUtils::Convert2Value(env, argv[1], bindArgs) == OK);
719 }
720 return OK;
721 }
722 std::string sql;
723 std::vector<ValueObject> bindArgs;
724
725 ValueObject output;
726 };
727
728 /*
729 * [JS API Prototype]
730 * [Promise]
731 * execute(sql: string, args?: Array<ValueType>): Promise<ValueType>;
732 */
Execute(napi_env env,napi_callback_info info)733 napi_value TransactionProxy::Execute(napi_env env, napi_callback_info info)
734 {
735 auto context = std::make_shared<ExecuteContext>();
736 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
737 context->Parse(env, argc, argv, self);
738 };
739 auto exec = [context]() -> int {
740 CHECK_RETURN_ERR(context->transaction_ != nullptr);
741 auto status = E_ERROR;
742 std::tie(status, context->output) = context->StealTransaction()->Execute(context->sql, context->bindArgs);
743 return status;
744 };
745 auto output = [context](napi_env env, napi_value &result) {
746 result = JSUtils::Convert2JSValue(env, context->output);
747 CHECK_RETURN_SET_E(result != nullptr, std::make_shared<InnerError>(E_ERROR));
748 };
749 context->SetAction(env, info, input, exec, output);
750
751 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
752 return ASYNC_CALL(env, context);
753 }
754 } // namespace OHOS::RelationalStoreJsKit