• 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         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