1 /*
2 * Copyright (c) 2025 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 #define LOG_TAG "GdbTransactionProxy"
16 #include "napi_gdb_transaction.h"
17
18 #include <algorithm>
19 #include <cinttypes>
20 #include <cstdint>
21 #include <vector>
22
23 #include "db_trace.h"
24 #include "js_utils.h"
25 #include "logger.h"
26 #include "napi_gdb_context.h"
27 #include "napi_gdb_error.h"
28 #include "napi_gdb_js_utils.h"
29
30 namespace OHOS::GraphStoreJsKit {
31 #define ASSERT_RETURN_SET_ERROR(assertion, paramError) \
32 CHECK_RETURN_CORE(assertion, SetError(paramError), ERR)
33
34 constexpr int32_t MAX_GQL_LEN = 1024 * 1024;
35
36 constexpr const char *SPACE_NAME = "ohos.data.graphStore";
37 constexpr const char *CLASS_NAME = "Transaction";
38
39 struct TransactionContext : public ContextBase {
GetInstanceOHOS::GraphStoreJsKit::TransactionContext40 void GetInstance(napi_value self)
41 {
42 auto status = napi_unwrap(env_, self, reinterpret_cast<void **>(&boundObj));
43 if (status != napi_ok || boundObj == nullptr) {
44 LOG_ERROR("GdbTransactionProxy native instance is nullptr! code:%{public}d!", status);
45 return;
46 }
47 transaction_ = reinterpret_cast<GdbTransactionProxy *>(boundObj)->GetInstance();
48 }
StealTransactionOHOS::GraphStoreJsKit::TransactionContext49 std::shared_ptr<Transaction> StealTransaction()
50 {
51 auto trans = std::move(transaction_);
52 transaction_ = nullptr;
53 return trans;
54 }
55 std::shared_ptr<Transaction> transaction_ = nullptr;
56 };
57
~GdbTransactionProxy()58 GdbTransactionProxy::~GdbTransactionProxy()
59 {
60 }
61
GdbTransactionProxy(std::shared_ptr<Transaction> gdbTransaction)62 GdbTransactionProxy::GdbTransactionProxy(std::shared_ptr<Transaction> gdbTransaction)
63 {
64 if (GetInstance() == gdbTransaction) {
65 return;
66 }
67 SetInstance(std::move(gdbTransaction));
68 }
69
Initialize(napi_env env,napi_callback_info info)70 napi_value GdbTransactionProxy::Initialize(napi_env env, napi_callback_info info)
71 {
72 napi_value self = nullptr;
73 NAPI_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &self, nullptr));
74 auto *proxy = new (std::nothrow) GdbTransactionProxy();
75 if (proxy == nullptr) {
76 return nullptr;
77 }
78 auto finalize = [](napi_env env, void *data, void *hint) {
79 if (data != hint) {
80 LOG_ERROR("memory corrupted! data:0x%016" PRIXPTR "hint:0x%016" PRIXPTR,
81 uintptr_t(data), uintptr_t(hint));
82 return;
83 }
84 GdbTransactionProxy *proxy = reinterpret_cast<GdbTransactionProxy *>(data);
85 proxy->SetInstance(nullptr);
86 delete proxy;
87 };
88 napi_status status = napi_wrap(env, self, proxy, finalize, proxy, nullptr);
89 if (status != napi_ok) {
90 LOG_ERROR("napi_wrap failed! code:%{public}d!", status);
91 finalize(env, proxy, proxy);
92 return nullptr;
93 }
94 return self;
95 }
96
Init(napi_env env,napi_value exports)97 void GdbTransactionProxy::Init(napi_env env, napi_value exports)
98 {
99 auto lambda = []() -> std::vector<napi_property_descriptor> {
100 std::vector<napi_property_descriptor> properties = {
101 DECLARE_NAPI_FUNCTION("read", Read),
102 DECLARE_NAPI_FUNCTION("write", Write),
103 DECLARE_NAPI_FUNCTION("commit", Commit),
104 DECLARE_NAPI_FUNCTION("rollback", Rollback),
105 };
106 return properties;
107 };
108 auto jsCtor = AppDataMgrJsKit::JSUtils::DefineClass(env, SPACE_NAME, CLASS_NAME, lambda, Initialize);
109 NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, exports, CLASS_NAME, jsCtor));
110 }
111
NewInstance(napi_env env,std::shared_ptr<Transaction> value)112 napi_value GdbTransactionProxy::NewInstance(napi_env env, std::shared_ptr<Transaction> value)
113 {
114 if (value == nullptr) {
115 LOG_ERROR("value is nullptr");
116 return nullptr;
117 }
118 napi_value cons = AppDataMgrJsKit::JSUtils::GetClass(env, SPACE_NAME, CLASS_NAME);
119 if (cons == nullptr) {
120 LOG_ERROR("Constructor of Transaction is nullptr!");
121 return nullptr;
122 }
123
124 napi_value instance = nullptr;
125 auto status = napi_new_instance(env, cons, 0, nullptr, &instance);
126 if (status != napi_ok) {
127 LOG_ERROR("create new instance failed! code:%{public}d!", status);
128 return nullptr;
129 }
130
131 GdbTransactionProxy *proxy = nullptr;
132 status = napi_unwrap(env, instance, reinterpret_cast<void **>(&proxy));
133 if (status != napi_ok || proxy == nullptr) {
134 LOG_ERROR("native instance is nullptr! code:%{public}d!", status);
135 return instance;
136 }
137 proxy->SetInstance(std::move(value));
138 return instance;
139 }
140
141 struct ReadWriteContext : public TransactionContext {
ParseOHOS::GraphStoreJsKit::ReadWriteContext142 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
143 {
144 ASSERT_RETURN_SET_ERROR(argc == 1, std::make_shared<ParamNumError>(" 1 "));
145 GetInstance(self);
146 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "not nullptr."));
147 gql = AppDataMgrJsKit::JSUtils::Convert2String(env, argv[0]);
148 ASSERT_RETURN_SET_ERROR(!gql.empty(), std::make_shared<ParamError>("gql", "not empty"));
149 ASSERT_RETURN_SET_ERROR(gql.size() <= MAX_GQL_LEN,
150 std::make_shared<ParamError>("gql", "too long"));
151 return OK;
152 }
153 std::string gql;
154 std::shared_ptr<Result> result;
155 int32_t errCode;
156 };
157
Read(napi_env env,napi_callback_info info)158 napi_value GdbTransactionProxy::Read(napi_env env, napi_callback_info info)
159 {
160 DISTRIBUTED_DATA_HITRACE(std::string(__FUNCTION__));
161 auto context = std::make_shared<ReadWriteContext>();
162 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
163 context->Parse(env, argc, argv, self);
164 };
165 auto exec = [context]() -> int {
166 CHECK_RETURN_ERR(context->transaction_ != nullptr);
167 std::tie(context->errCode, context->result) = context->StealTransaction()->Query(context->gql);
168 return context->errCode;
169 };
170 auto output = [context](napi_env env, napi_value &result) {
171 result = AppDataMgrJsKit::JSUtils::Convert2JSValue(env, context->result);
172 CHECK_RETURN_SET_E(context->errCode == OK, std::make_shared<InnerError>(context->errCode));
173 };
174 context->SetAction(env, info, input, exec, output);
175
176 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
177 return ASYNC_CALL(env, context);
178 }
179
Write(napi_env env,napi_callback_info info)180 napi_value GdbTransactionProxy::Write(napi_env env, napi_callback_info info)
181 {
182 DISTRIBUTED_DATA_HITRACE(std::string(__FUNCTION__));
183 auto context = std::make_shared<ReadWriteContext>();
184 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
185 context->Parse(env, argc, argv, self);
186 };
187 auto exec = [context]() -> int {
188 CHECK_RETURN_ERR(context->transaction_ != nullptr);
189 std::tie(context->errCode, context->result) = context->StealTransaction()->Execute(context->gql);
190 return context->errCode;
191 };
192 auto output = [context](napi_env env, napi_value &result) {
193 result = AppDataMgrJsKit::JSUtils::Convert2JSValue(env, context->result);
194 CHECK_RETURN_SET_E(context->errCode == OK, std::make_shared<InnerError>(context->errCode));
195 };
196 context->SetAction(env, info, input, exec, output);
197
198 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
199 return ASYNC_CALL(env, context);
200 }
201
202 struct CommitRollbackContext : public TransactionContext {
ParseOHOS::GraphStoreJsKit::CommitRollbackContext203 int32_t Parse(napi_env env, size_t argc, napi_value *argv, napi_value self)
204 {
205 GetInstance(self);
206 ASSERT_RETURN_SET_ERROR(transaction_ != nullptr, std::make_shared<ParamError>("transaction", "a transaction."));
207 return OK;
208 }
209 };
210
Commit(napi_env env,napi_callback_info info)211 napi_value GdbTransactionProxy::Commit(napi_env env, napi_callback_info info)
212 {
213 DISTRIBUTED_DATA_HITRACE(std::string(__FUNCTION__));
214 auto context = std::make_shared<CommitRollbackContext>();
215 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
216 context->Parse(env, argc, argv, self);
217 };
218 auto exec = [context]() -> int {
219 CHECK_RETURN_ERR(context->transaction_ != nullptr);
220 return context->StealTransaction()->Commit();
221 };
222 auto output = [context](napi_env env, napi_value &result) {
223 napi_status status = napi_get_undefined(env, &result);
224 CHECK_RETURN_SET_E(status == napi_ok, std::make_shared<InnerError>(E_ERROR));
225 };
226 context->SetAction(env, info, input, exec, output);
227
228 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
229 return ASYNC_CALL(env, context);
230 }
231
Rollback(napi_env env,napi_callback_info info)232 napi_value GdbTransactionProxy::Rollback(napi_env env, napi_callback_info info)
233 {
234 DISTRIBUTED_DATA_HITRACE(std::string(__FUNCTION__));
235 auto context = std::make_shared<CommitRollbackContext>();
236 auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) {
237 context->Parse(env, argc, argv, self);
238 };
239 auto exec = [context]() -> int {
240 CHECK_RETURN_ERR(context->transaction_ != nullptr);
241 return context->StealTransaction()->Rollback();
242 };
243 auto output = [context](napi_env env, napi_value &result) {
244 napi_status status = napi_get_undefined(env, &result);
245 CHECK_RETURN_SET_E(status == napi_ok, std::make_shared<InnerError>(E_ERROR));
246 };
247 context->SetAction(env, info, input, exec, output);
248
249 CHECK_RETURN_NULL(context->error == nullptr || context->error->GetCode() == OK);
250 return ASYNC_CALL(env, context);
251 }
252 } // namespace OHOS::GraphStoreJsKit
253