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