* 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<Asset>;
using FloatVector = std::vector<float>;
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<napi_property_descriptor> 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<uint8_t> &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<uint8_t *>(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<float> &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<ResultSet> 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,
JS_E_INNER_FAIL, "NAPI napi_get_cb_info failed");
CHECK_COND_WITH_ERR_MESSAGE(env, napi_get_value_int32(env, args[0], &ret) == napi_ok, JS_E_PARAM_INVALID,
"NAPI napi_get_value_int32 failed");
ResultSetNapi *obj = nullptr;
status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
CHECK_COND_WITH_ERR_MESSAGE(env, status == napi_ok && obj != nullptr, JS_E_INNER_FAIL, "NAPI env error");
std::shared_ptr<ResultSet> rs = obj->resultSetPtr;
CHECK_COND_WITH_ERR_MESSAGE(env, rs != nullptr, JS_E_INNER_FAIL, "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, JS_E_INNER_FAIL, "GET_JS_OBJ_WITH_ZERO_ARGS failed");
ResultSetNapi *obj = nullptr;
status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
CHECK_COND_WITH_ERR_MESSAGE(env, status == napi_ok && obj != nullptr, JS_E_INNER_FAIL, "NAPI_unwrap failed");
return obj;
}
static std::shared_ptr<ResultSet> GetResultSet(napi_env env, napi_callback_info info)
{
ResultSetNapi *obj = GetResultSetNapi(env, info);
CHECK_COND_WITH_ERR_MESSAGE(env, obj != nullptr, JS_E_INNER_FAIL, "ResultSetNapi is nullptr");
std::shared_ptr<ResultSet> rs = obj->resultSetPtr;
CHECK_COND_WITH_ERR_MESSAGE(env, rs != nullptr, JS_E_INNER_FAIL, "ResultSet is nullptr");
return rs;
}
ResultSetNapi::ResultSetNapi(std::shared_ptr<ResultSet> 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<ResultSetNapi *>(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,
JS_E_INNER_FAIL, "Napi env error");
return thisArg;
}
napi_value ResultSetNapi::CreateResultSetNapi(napi_env env, std::shared_ptr<ResultSet> &resultSet,
JSAsyncContextOutput &asyncContext)
{
if (resultSet == nullptr) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, asyncContext.error, JS_E_INNER_FAIL, "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, JS_E_INNER_FAIL,
"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, JS_E_INNER_FAIL,
"Napi env error: Napi_new_instance");
return nullptr;
}
ResultSetNapi *obj = new ResultSetNapi(resultSet);
if (obj == nullptr) {
MediaLibraryNapiUtils::CreateNapiErrorObject(env, asyncContext.error, JS_E_INNER_FAIL,
"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, JS_E_INNER_FAIL, "Napi env error");
return nullptr;
}
return instance;
}
napi_value ResultSetNapi::JSGetColumnCount(napi_env env, napi_callback_info info)
{
std::shared_ptr<ResultSet> 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> 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> 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> 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> 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> 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> 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> resultset = GetInt32AndResultSet(env, info, columnIndex);
CHECK_COND_WITH_ERR_MESSAGE(env, resultset != nullptr, JS_E_INNER_FAIL, "Resultset is nullptr");
std::vector<uint8_t> 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> 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> 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> 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> 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> 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> 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 = nullptr;
} else {
NAPI_WARN_LOG("Resultset is already closed");
}
return nullptr;
}
}
}