• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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