/* * Copyright (C) 2025-2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "result_set_napi.h" #include "medialibrary_client_errno.h" #include "medialibrary_napi_log.h" #include "big_integer.h" #include "asset_value.h" using OHOS::HiviewDFX::HiLog; using OHOS::HiviewDFX::HiLogLabel; namespace OHOS { namespace Media { using namespace std; using namespace OHOS::NativeRdb; using Asset = AssetValue; using BigInt = BigInteger; using Assets = std::vector; using FloatVector = std::vector; thread_local napi_ref ResultSetNapi::sResultSetConstructor_ = nullptr; template <> napi_value Convert2JSValue(napi_env env, const int32_t &value) { napi_value jsValue = nullptr; if (napi_create_int32(env, value, &jsValue) != napi_ok) { NAPI_ERR_LOG("Napi_create_int32 failed"); return jsValue; } return jsValue; } template <> napi_value Convert2JSValue(napi_env env, const uint32_t &value) { napi_value jsValue = nullptr; if (napi_create_uint32(env, value, &jsValue) != napi_ok) { NAPI_ERR_LOG("Napi_create_uint32 failed"); return jsValue; } return jsValue; } template <> napi_value Convert2JSValue(napi_env env, const int64_t &value) { napi_value jsValue = nullptr; if (napi_create_int64(env, value, &jsValue) != napi_ok) { NAPI_ERR_LOG("Napi_create_int64 failed"); return jsValue; } return jsValue; } template <> napi_value Convert2JSValue(napi_env env, const std::string &value) { napi_value jsValue = nullptr; if (napi_create_string_utf8(env, value.c_str(), value.size(), &jsValue) != napi_ok) { NAPI_ERR_LOG("Napi_create_string failed"); return jsValue; } return jsValue; } template <> napi_value Convert2JSValue(napi_env env, const Asset &value) { auto outputStatus = value.status & ~0xF0000000; std::vector descriptors = { napi_property_descriptor(DECLARE_NAPI_DEFAULT_PROPERTY(("name"), Convert2JSValue((env), (value.name)))), napi_property_descriptor(DECLARE_NAPI_DEFAULT_PROPERTY(("uri"), Convert2JSValue((env), (value.uri)))), napi_property_descriptor(DECLARE_NAPI_DEFAULT_PROPERTY(("createTime"), Convert2JSValue((env), (value.createTime)))), napi_property_descriptor(DECLARE_NAPI_DEFAULT_PROPERTY(("modifyTime"), Convert2JSValue((env), (value.modifyTime)))), napi_property_descriptor(DECLARE_NAPI_DEFAULT_PROPERTY(("size"), Convert2JSValue((env), (value.size)))), napi_property_descriptor(DECLARE_NAPI_DEFAULT_PROPERTY(("path"), Convert2JSValue((env), (value.path)))), napi_property_descriptor(DECLARE_NAPI_DEFAULT_PROPERTY(("status"), Convert2JSValue((env), (outputStatus)))), }; napi_value object = nullptr; if (napi_create_object_with_properties(env, &object, descriptors.size(), descriptors.data()) != napi_ok) { NAPI_ERR_LOG("Napi_create_Asset failed"); return object; } return object; } template <> napi_value Convert2JSValue(napi_env env, const double &value) { napi_value jsValue = nullptr; if (napi_create_double(env, value, &jsValue) != napi_ok) { NAPI_ERR_LOG("Napi_create_double failed"); return jsValue; } return jsValue; } template <> napi_value Convert2JSValue(napi_env env, const bool &value) { napi_value jsValue = nullptr; if (napi_get_boolean(env, value, &jsValue) != napi_ok) { NAPI_ERR_LOG("Napi_create_boolean failed"); return jsValue; } return jsValue; } template <> napi_value Convert2JSValue(napi_env env, const float &value) { napi_value jsValue = nullptr; if (napi_create_double(env, value, &jsValue) != napi_ok) { NAPI_ERR_LOG("Napi_create_float failed"); return jsValue; } return jsValue; } template <> napi_value Convert2JSValue(napi_env env, const std::vector &value) { size_t size = value.size(); void *data = nullptr; napi_value buffer = nullptr; if (napi_create_arraybuffer(env, size, &data, &buffer) != napi_ok) { NAPI_ERR_LOG("Napi_create_buffer failed"); return buffer; } if (size != 0 && data) { std::copy(value.begin(), value.end(), static_cast(data)); } else { NAPI_ERR_LOG("Data is empty"); } napi_value napiValue = nullptr; if (napi_create_typedarray(env, napi_uint8_array, size, buffer, 0, &napiValue) != napi_ok) { NAPI_ERR_LOG("Napi_create_buffer failed"); return buffer; } return napiValue; } template <> napi_value Convert2JSValue(napi_env env, const BigInt &value) { napi_value jsValue = nullptr; if (napi_create_bigint_words(env, value.Sign(), value.Size(), value.TrueForm(), &jsValue) != napi_ok) { NAPI_ERR_LOG("Napi_create_BigInt failed"); return jsValue; } return jsValue; } template <> napi_value Convert2JSValue(napi_env env, const std::vector &value) { napi_value jsValue = nullptr; float *native = nullptr; napi_value buffer = nullptr; napi_status status = napi_create_arraybuffer(env, value.size() * sizeof(float), (void **)&native, &buffer); if (status != napi_ok || native == nullptr) { NAPI_ERR_LOG("Napi_create_FLOAT_VECTOR failed"); return jsValue; } for (size_t i = 0; i < value.size(); i++) { *(native + i) = value[i]; } status = napi_create_typedarray(env, napi_float32_array, value.size(), buffer, 0, &jsValue); if (status != napi_ok) { NAPI_ERR_LOG("Napi_create_FLOAT_VECTOR failed"); return jsValue; } return jsValue; } template <> napi_value Convert2JSValue(napi_env env, const ValueObject &value) { return Convert2JSValue(env, value.value); } template <> napi_value Convert2JSValue(napi_env env, const RowEntity &rowEntity) { napi_value ret = nullptr; if (napi_create_object(env, &ret) != napi_ok) { NAPI_ERR_LOG("Napi_create_object failed"); return ret; } auto &values = rowEntity.Get(); for (auto const &[key, object] : values) { napi_value value = Convert2JSValue(env, object.value); napi_set_named_property(env, ret, key.c_str(), value); } return ret; } static std::shared_ptr GetInt32AndResultSet(napi_env env, napi_callback_info info, int32_t &ret) { napi_status status; napi_value thisVar = nullptr; size_t argc = 1; napi_value args[1]; CHECK_COND_WITH_ERR_MESSAGE(env, napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr) == napi_ok, UFM_SYSCAP_BASE, "NAPI napi_get_cb_info failed"); CHECK_COND_WITH_ERR_MESSAGE(env, napi_get_value_int32(env, args[0], &ret) == napi_ok, UFM_SYSCAP_BASE, "NAPI napi_get_value_int32 failed"); ResultSetNapi *obj = nullptr; status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); CHECK_COND_WITH_ERR_MESSAGE(env, status == napi_ok && obj != nullptr, UFM_SYSCAP_BASE, "napi env error"); std::shared_ptr rs = obj->resultSetPtr; CHECK_COND_WITH_ERR_MESSAGE(env, rs != nullptr, UFM_SYSCAP_BASE, "ResultSet is null"); return rs; } static ResultSetNapi *GetResultSetNapi(napi_env env, napi_callback_info info) { napi_status status; napi_value thisVar = nullptr; GET_JS_OBJ_WITH_ZERO_ARGS(env, info, status, thisVar); CHECK_COND_WITH_ERR_MESSAGE(env, status == napi_ok, UFM_SYSCAP_BASE, "GET_JS_OBJ_WITH_ZERO_ARGS failed"); ResultSetNapi *obj = nullptr; status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); CHECK_COND_WITH_ERR_MESSAGE(env, status == napi_ok && obj != nullptr, UFM_SYSCAP_BASE, "napi_unwrap failed"); return obj; } static std::shared_ptr GetResultSet(napi_env env, napi_callback_info info) { ResultSetNapi *obj = GetResultSetNapi(env, info); CHECK_COND_WITH_ERR_MESSAGE(env, obj != nullptr, UFM_SYSCAP_BASE, "ResultSetNapi is nullptr"); std::shared_ptr rs = obj->resultSetPtr; CHECK_COND_WITH_ERR_MESSAGE(env, rs != nullptr, UFM_SYSCAP_BASE, "ResultSet is nullptr"); return rs; } ResultSetNapi::ResultSetNapi(std::shared_ptr ptr) : resultSetPtr(std::move(ptr)), env_(nullptr) {} ResultSetNapi::~ResultSetNapi() = default; void ResultSetNapi::ResultSetNapiDestructor(napi_env env, void *nativeObject, void *finalize_hint) { ResultSetNapi *resultSet = reinterpret_cast(nativeObject); if (resultSet != nullptr) { delete resultSet; } else { NAPI_ERR_LOG("ResultSet is nullptr, invalid nativeObject in Finalizer"); } } napi_value ResultSetNapi::Init(napi_env env, napi_value exports) { NapiClassInfo info = {.name = RESULT_SET_NAPI_CLASS_NAME, .ref = &sResultSetConstructor_, .constructor = ResultSetNapiConstructor, .props = { // 属性 DECLARE_NAPI_GETTER("columnCount", JSGetColumnCount), DECLARE_NAPI_GETTER("rowCount", JSGetRowCount), DECLARE_NAPI_GETTER("rowIndex", JSGetRowIndex), DECLARE_NAPI_GETTER("isAtLastRow", JSIsAtLastRow), // 方法 DECLARE_NAPI_FUNCTION("goToRow", JSGoToRow), DECLARE_NAPI_FUNCTION("goToFirstRow", JSGoToFirstRow), DECLARE_NAPI_FUNCTION("goToNextRow", JSGoToNextRow), DECLARE_NAPI_FUNCTION("getBlob", JSGetBlob), DECLARE_NAPI_FUNCTION("getString", JSGetString), DECLARE_NAPI_FUNCTION("getLong", JSGetLong), DECLARE_NAPI_FUNCTION("getDouble", JSGetDouble), DECLARE_NAPI_FUNCTION("getBool", JSGetBool), DECLARE_NAPI_FUNCTION("getValue", JSGetValue), DECLARE_NAPI_FUNCTION("getRow", JSGetRow), DECLARE_NAPI_FUNCTION("close", JSClose), }}; MediaLibraryNapiUtils::NapiDefineClass(env, exports, info); return exports; } napi_value ResultSetNapi::ResultSetNapiConstructor(napi_env env, napi_callback_info info) { napi_value thisArg; CHECK_COND_WITH_ERR_MESSAGE(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) == napi_ok, UFM_SYSCAP_BASE, "Napi env error"); return thisArg; } napi_value ResultSetNapi::CreateResultSetNapi(napi_env env, std::shared_ptr &resultSet, JSAsyncContextOutput &asyncContext) { if (resultSet == nullptr) { MediaLibraryNapiUtils::CreateNapiErrorObject(env, asyncContext.error, UFM_SYSCAP_BASE, "ResultSet is null"); return nullptr; } napi_value constructor; napi_status status = napi_get_reference_value(env, sResultSetConstructor_, &constructor); if (status != napi_ok) { MediaLibraryNapiUtils::CreateNapiErrorObject(env, asyncContext.error, UFM_SYSCAP_BASE, "Napi env error: Napi_get_reference_value"); return nullptr; } napi_value instance; status = napi_new_instance(env, constructor, 0, nullptr, &instance); if (status != napi_ok) { MediaLibraryNapiUtils::CreateNapiErrorObject(env, asyncContext.error, UFM_SYSCAP_BASE, "Napi env error: Napi_new_instance"); return nullptr; } ResultSetNapi *obj = new ResultSetNapi(resultSet); if (obj == nullptr) { MediaLibraryNapiUtils::CreateNapiErrorObject(env, asyncContext.error, UFM_SYSCAP_BASE, "ResultSetnapi create failed"); return nullptr; } obj->env_ = env; status = napi_wrap(env, instance, obj, ResultSetNapi::ResultSetNapiDestructor, nullptr, nullptr); if (status != napi_ok) { delete obj; MediaLibraryNapiUtils::CreateNapiErrorObject(env, asyncContext.error, UFM_SYSCAP_BASE, "Napi env error"); return nullptr; } return instance; } napi_value ResultSetNapi::JSGetColumnCount(napi_env env, napi_callback_info info) { std::shared_ptr resultset = GetResultSet(env, info); CHECK_COND_WITH_ERR_MESSAGE(env, resultset != nullptr, JS_E_INNER_FAIL, "Resultset is nullptr"); int32_t count = 0; CHECK_COND_WITH_ERR_MESSAGE(env, resultset->GetColumnCount(count) == E_OK, JS_E_INNER_FAIL, "Database error"); return Convert2JSValue(env, count); } napi_value ResultSetNapi::JSGetRowCount(napi_env env, napi_callback_info info) { std::shared_ptr resultset = GetResultSet(env, info); CHECK_COND_WITH_ERR_MESSAGE(env, resultset != nullptr, JS_E_INNER_FAIL, "Resultset is nullptr"); int32_t count = 0; CHECK_COND_WITH_ERR_MESSAGE(env, resultset->GetRowCount(count) == E_OK, JS_E_INNER_FAIL, "Database error"); return Convert2JSValue(env, count); } napi_value ResultSetNapi::JSGetRowIndex(napi_env env, napi_callback_info info) { std::shared_ptr resultset = GetResultSet(env, info); CHECK_COND_WITH_ERR_MESSAGE(env, resultset != nullptr, JS_E_INNER_FAIL, "Resultset is nullptr"); int32_t index = 0; CHECK_COND_WITH_ERR_MESSAGE(env, resultset->GetRowIndex(index) == E_OK, JS_E_INNER_FAIL, "Database error"); return Convert2JSValue(env, index); } napi_value ResultSetNapi::JSIsAtLastRow(napi_env env, napi_callback_info info) { std::shared_ptr resultset = GetResultSet(env, info); CHECK_COND_WITH_ERR_MESSAGE(env, resultset != nullptr, JS_E_INNER_FAIL, "Resultset is nullptr"); bool isAtLast = false; CHECK_COND_WITH_ERR_MESSAGE(env, resultset->IsAtLastRow(isAtLast) == E_OK, JS_E_INNER_FAIL, "Database error"); return Convert2JSValue(env, isAtLast); } // 方法实现 napi_value ResultSetNapi::JSGoToRow(napi_env env, napi_callback_info info) { int32_t position; int32_t rowCount; std::shared_ptr resultset = GetInt32AndResultSet(env, info, position); CHECK_COND_WITH_ERR_MESSAGE(env, resultset != nullptr, JS_E_INNER_FAIL, "Resultset is nullptr"); CHECK_COND_WITH_ERR_MESSAGE(env, resultset->GetRowCount(rowCount) == E_OK, JS_E_INNER_FAIL, "Database error"); int err = resultset->GoToRow(position); CHECK_COND_WITH_ERR_MESSAGE(env, err != E_ROW_OUT_RANGE, JS_E_PARAM_INVALID, "Index out of range"); CHECK_COND_WITH_ERR_MESSAGE(env, err == E_OK, JS_E_INNER_FAIL, "Database error"); return Convert2JSValue(env, true); } napi_value ResultSetNapi::JSGoToFirstRow(napi_env env, napi_callback_info info) { std::shared_ptr resultset = GetResultSet(env, info); CHECK_COND_WITH_ERR_MESSAGE(env, resultset != nullptr, JS_E_INNER_FAIL, "Resultset is nullptr"); CHECK_COND_WITH_ERR_MESSAGE(env, resultset->GoToFirstRow() == E_OK, JS_E_INNER_FAIL, "Database error"); return Convert2JSValue(env, true); } napi_value ResultSetNapi::JSGoToNextRow(napi_env env, napi_callback_info info) { std::shared_ptr resultset = GetResultSet(env, info); CHECK_COND_WITH_ERR_MESSAGE(env, resultset != nullptr, JS_E_INNER_FAIL, "Resultset is nullptr"); int err = resultset->GoToNextRow(); if (err == E_ROW_OUT_RANGE) { return Convert2JSValue(env, false); } CHECK_COND_WITH_ERR_MESSAGE(env, err == E_OK, JS_E_INNER_FAIL, "Database error"); return Convert2JSValue(env, true); } napi_value ResultSetNapi::JSGetBlob(napi_env env, napi_callback_info info) { int32_t columnIndex; std::shared_ptr resultset = GetInt32AndResultSet(env, info, columnIndex); CHECK_COND_WITH_ERR_MESSAGE(env, resultset != nullptr, JS_E_INNER_FAIL, "Resultset is nullptr"); std::vector result; int err = resultset->GetBlob(columnIndex, result); CHECK_COND_WITH_ERR_MESSAGE(env, err != E_COLUMN_OUT_RANGE, JS_E_PARAM_INVALID, "Column index of out range"); CHECK_COND_WITH_ERR_MESSAGE(env, err == E_OK, JS_E_INNER_FAIL, "Database error"); return Convert2JSValue(env, result); } napi_value ResultSetNapi::JSGetString(napi_env env, napi_callback_info info) { int32_t columnIndex; std::shared_ptr resultset = GetInt32AndResultSet(env, info, columnIndex); CHECK_COND_WITH_ERR_MESSAGE(env, resultset != nullptr, JS_E_INNER_FAIL, "Resultset is nullptr"); std::string result; int err = resultset->GetString(columnIndex, result); CHECK_COND_WITH_ERR_MESSAGE(env, err != E_COLUMN_OUT_RANGE, JS_E_PARAM_INVALID, "Column index of out range"); CHECK_COND_WITH_ERR_MESSAGE(env, err == E_OK, JS_E_INNER_FAIL, "Database error"); return Convert2JSValue(env, result); } napi_value ResultSetNapi::JSGetLong(napi_env env, napi_callback_info info) { int32_t columnIndex; std::shared_ptr resultset = GetInt32AndResultSet(env, info, columnIndex); CHECK_COND_WITH_ERR_MESSAGE(env, resultset != nullptr, JS_E_INNER_FAIL, "Resultset is nullptr"); int64_t result; int err = resultset->GetLong(columnIndex, result); CHECK_COND_WITH_ERR_MESSAGE(env, err != E_COLUMN_OUT_RANGE, JS_E_PARAM_INVALID, "Column index of out range"); CHECK_COND_WITH_ERR_MESSAGE(env, err == E_OK, JS_E_INNER_FAIL, "Database error"); return Convert2JSValue(env, result); } napi_value ResultSetNapi::JSGetDouble(napi_env env, napi_callback_info info) { int32_t columnIndex; std::shared_ptr resultset = GetInt32AndResultSet(env, info, columnIndex); CHECK_COND_WITH_ERR_MESSAGE(env, resultset != nullptr, JS_E_INNER_FAIL, "Resultset is nullptr"); double result; int err = resultset->GetDouble(columnIndex, result); CHECK_COND_WITH_ERR_MESSAGE(env, err != E_COLUMN_OUT_RANGE, JS_E_PARAM_INVALID, "Column index of out range"); CHECK_COND_WITH_ERR_MESSAGE(env, err == E_OK, JS_E_INNER_FAIL, "Database error"); return Convert2JSValue(env, result); } napi_value ResultSetNapi::JSGetBool(napi_env env, napi_callback_info info) { int32_t columnIndex; std::shared_ptr resultset = GetInt32AndResultSet(env, info, columnIndex); CHECK_COND_WITH_ERR_MESSAGE(env, resultset != nullptr, JS_E_INNER_FAIL, "Resultset is nullptr"); ValueObject result; int err = resultset->Get(columnIndex, result); CHECK_COND_WITH_ERR_MESSAGE(env, err != E_COLUMN_OUT_RANGE, JS_E_PARAM_INVALID, "Column index of out range"); bool boolResult; err = result.GetBool(boolResult); CHECK_COND_WITH_ERR_MESSAGE(env, err == E_OK, JS_E_INNER_FAIL, "Database error"); return Convert2JSValue(env, boolResult); } napi_value ResultSetNapi::JSGetValue(napi_env env, napi_callback_info info) { int32_t columnIndex; std::shared_ptr resultset = GetInt32AndResultSet(env, info, columnIndex); CHECK_COND_WITH_ERR_MESSAGE(env, resultset != nullptr, JS_E_INNER_FAIL, "Resultset is nullptr"); ValueObject result; int err = resultset->Get(columnIndex, result); CHECK_COND_WITH_ERR_MESSAGE(env, err != E_COLUMN_OUT_RANGE, JS_E_PARAM_INVALID, "Column index of out range"); CHECK_COND_WITH_ERR_MESSAGE(env, err == E_OK, JS_E_INNER_FAIL, "Database error"); return Convert2JSValue(env, result); } napi_value ResultSetNapi::JSGetRow(napi_env env, napi_callback_info info) { std::shared_ptr resultset = GetResultSet(env, info); CHECK_COND_WITH_ERR_MESSAGE(env, resultset != nullptr, JS_E_INNER_FAIL, "Resultset is nullptr"); RowEntity rowEntity; CHECK_COND_WITH_ERR_MESSAGE(env, resultset->GetRow(rowEntity) == E_OK, JS_E_INNER_FAIL, "Database error"); return Convert2JSValue(env, rowEntity); } napi_value ResultSetNapi::JSClose(napi_env env, napi_callback_info info) { ResultSetNapi *obj = GetResultSetNapi(env, info); if (obj->resultSetPtr != nullptr) { obj->resultSetPtr = move(nullptr); } return nullptr; } } // namespace Media } // namespace OHOS