/* -------------------------------------------------------------------------
 *  This file is part of the oGRAC project.
 * Copyright (c) 2024 Huawei Technologies Co.,Ltd.
 *
 * oGRAC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 *
 * pl_library.c
 *
 *
 * IDENTIFICATION
 * src/ogsql/pl/meta/pl_library.c
 *
 * -------------------------------------------------------------------------
 */

#include "pl_library.h"
#include "knl_session.h"
#include "knl_interface.h"
#include "knl_database.h"
#include "knl_context.h"
#include "knl_dc.h"
#include "pl_ext_proc.h"
#include "dtc_dls.h"

status_t pl_find_library(knl_handle_t se, uint32 uid, text_t *name, pl_library_t *library, bool32 *exists)
{
    knl_session_t *session = (knl_session_t *)se;
    knl_cursor_t *cur = NULL;
    text_t path_text;
    text_t agent_text;
    text_t leaf_text;
    *exists = OG_FALSE;

    CM_SAVE_STACK(session->stack);

    cur = knl_push_cursor(session);

    knl_open_sys_cursor(session, cur, CURSOR_ACTION_SELECT, SYS_LIBRARY_ID, IDX_LIBRARY_001_ID);
    knl_init_index_scan(cur, OG_TRUE);
    knl_set_scan_key(INDEX_DESC(cur->index), &cur->scan_range.l_key, OG_TYPE_INTEGER, (void *)&uid,
        sizeof(uint32), IX_COL_SYS_LIBRARY001_OWNER);
    knl_set_scan_key(INDEX_DESC(cur->index), &cur->scan_range.l_key, OG_TYPE_STRING, name->str, name->len,
        IX_COL_SYS_LIBRARY001_NAME);

    if (knl_fetch(session, cur) != OG_SUCCESS) {
        CM_RESTORE_STACK(session->stack);
        return OG_ERROR;
    }

    if (cur->eof) {
        CM_RESTORE_STACK(session->stack);
        return OG_SUCCESS;
    }
    *exists = OG_TRUE;
    if (library != NULL) {
        // get library uid and name
        library->uid = uid;
        cm_text2str(name, library->name, OG_NAME_BUFFER_SIZE);

        // get library info
        path_text.len = (uint32)CURSOR_COLUMN_SIZE(cur, SYS_LIBRARY_FILE_PATH);
        path_text.str = (char *)CURSOR_COLUMN_DATA(cur, SYS_LIBRARY_FILE_PATH);

        library->status = *(uint32 *)CURSOR_COLUMN_DATA(cur, SYS_LIBRARY_STATUS);
        library->flags = *(uint32 *)CURSOR_COLUMN_DATA(cur, SYS_LIBRARY_FLAGS);

        agent_text.len = (uint32)CURSOR_COLUMN_SIZE(cur, SYS_LIBRARY_AGENT_DBLINK);
        agent_text.str = (char *)CURSOR_COLUMN_DATA(cur, SYS_LIBRARY_AGENT_DBLINK);

        leaf_text.len = (uint32)CURSOR_COLUMN_SIZE(cur, SYS_LIBRARY_LEAF_FILENAME);
        leaf_text.str = (char *)CURSOR_COLUMN_DATA(cur, SYS_LIBRARY_LEAF_FILENAME);

        library->chg_scn = *(int64 *)CURSOR_COLUMN_DATA(cur, SYS_LIBRARY_ORG_SCN);
        library->org_scn = *(int64 *)CURSOR_COLUMN_DATA(cur, SYS_LIBRARY_CHG_SCN);

        cm_text2str(&path_text, library->path, OG_FILE_NAME_BUFFER_SIZE);
        cm_text2str(&agent_text, library->agent_name, OG_FILE_NAME_BUFFER_SIZE);
        cm_text2str(&leaf_text, library->leaf_name, OG_NAME_BUFFER_SIZE);
    }

    CM_RESTORE_STACK(session->stack);
    return OG_SUCCESS;
}

static inline status_t pl_init_library_desc(knl_session_t *session, pl_library_t *library, pl_library_def_t *def)
{
    library->org_scn = db_inc_scn(session);
    library->chg_scn = library->org_scn;
    library->flags = 0;
    library->is_dll = OG_TRUE;
    OG_RETURN_IFERR(cm_text2str(&def->name, library->name, OG_NAME_BUFFER_SIZE));
    OG_RETURN_IFERR(cm_text2str(&def->leaf_name, library->leaf_name, OG_NAME_BUFFER_SIZE));
    OG_RETURN_IFERR(cm_text2str(&def->path, library->path, OG_FILE_NAME_BUFFER_SIZE));
    OG_RETURN_IFERR(cm_text2str(&def->agent, library->agent_name, OG_FILE_NAME_BUFFER_SIZE));
    library->status = OBJ_STATUS_VALID;

    return OG_SUCCESS;
}


static status_t pl_write_syslibrary(knl_session_t *session, knl_cursor_t *cursor, pl_library_t *library)
{
    uint32 max_size;
    row_assist_t ra;
    table_t *table = NULL;

    max_size = session->kernel->attr.max_row_size;
    knl_open_sys_cursor(session, cursor, CURSOR_ACTION_INSERT, SYS_LIBRARY_ID, OG_INVALID_ID32);
    table = (table_t *)cursor->table;
    row_init(&ra, cursor->buf, max_size, table->desc.column_count);

    OG_RETURN_IFERR(row_put_uint32(&ra, library->uid));

    OG_RETURN_IFERR(row_put_str(&ra, library->name));

    OG_RETURN_IFERR(row_put_str(&ra, library->path));

    OG_RETURN_IFERR(row_put_int32(&ra, library->flags));

    OG_RETURN_IFERR(row_put_int32(&ra, library->status));

    OG_RETURN_IFERR(row_put_str(&ra, library->agent_name));

    OG_RETURN_IFERR(row_put_str(&ra, library->leaf_name));

    OG_RETURN_IFERR(row_put_int64(&ra, library->org_scn));

    OG_RETURN_IFERR(row_put_int64(&ra, library->chg_scn));

    return knl_internal_insert(session, cursor);
}

static status_t pl_delete_from_syslibrary(knl_session_t *session, knl_cursor_t *cursor, text_t *owner,
    pl_library_t *library)
{
    knl_open_sys_cursor(session, cursor, CURSOR_ACTION_DELETE, SYS_LIBRARY_ID, IDX_LIBRARY_001_ID);
    knl_init_index_scan(cursor, OG_TRUE);
    knl_set_scan_key(INDEX_DESC(cursor->index), &cursor->scan_range.l_key, OG_TYPE_INTEGER, (void *)&library->uid,
        sizeof(uint32), IX_COL_SYS_LIBRARY001_OWNER);

    knl_set_scan_key(INDEX_DESC(cursor->index), &cursor->scan_range.l_key, OG_TYPE_STRING, (void *)library->name,
        (uint16)strlen(library->name), IX_COL_SYS_LIBRARY001_NAME);

    if (knl_fetch(session, cursor) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (cursor->eof) {
        OG_THROW_ERROR(ERR_LIBRARY_NOT_EXIST, T2S(owner), library->name);
        return OG_ERROR;
    }

    return knl_internal_delete(session, cursor);
}

static status_t pl_drop_library_internal(knl_session_t *session, text_t *owner, pl_library_t *library)
{
    knl_cursor_t *cursor = NULL;
    CM_SAVE_STACK(session->stack);
    cursor = knl_push_cursor(session);
    cursor->row = (row_head_t *)cursor->buf;

    if (db_drop_object_privs(session, library->uid, library->name, OBJ_TYPE_LIBRARY) != OG_SUCCESS) {
        CM_RESTORE_STACK(session->stack);
        return OG_ERROR;
    }

    if (pl_delete_from_syslibrary(session, cursor, owner, library) != OG_SUCCESS) {
        CM_RESTORE_STACK(session->stack);
        return OG_ERROR;
    }
    knl_commit(session);
    dc_drop_object_privs(&session->kernel->dc_ctx, library->uid, library->name, OBJ_TYPE_LIBRARY);
    CM_RESTORE_STACK(session->stack);

    return OG_SUCCESS;
}

status_t pl_create_library(knl_handle_t se, pl_library_def_t *def)
{
    knl_session_t *session = (knl_session_t *)se;
    pl_library_t library;
    knl_cursor_t *cursor = NULL;
    dc_user_t *user = NULL;
    drlatch_t *ddl_latch = &session->kernel->db.ddl_latch;
    if (knl_ddl_enabled(session, OG_TRUE) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (dc_open_user(session, &def->owner, &user) != OG_SUCCESS) {
        return OG_ERROR;
    }
    dls_latch_x(session, &user->user_latch, session->id, NULL);
    dls_latch_s(session, ddl_latch, session->id, OG_FALSE, NULL);

    bool32 exists = OG_FALSE;
    library.uid = user->desc.id;
    if (pl_find_library(se, library.uid, &def->name, &library, &exists) != OG_SUCCESS) {
        dls_unlatch(session, ddl_latch, NULL);
        dls_unlatch(session, &user->user_latch, NULL);
        return OG_ERROR;
    }

    if (exists) {
        if (!def->is_replace) {
            dls_unlatch(session, ddl_latch, NULL);
            dls_unlatch(session, &user->user_latch, NULL);

            OG_THROW_ERROR(ERR_OBJECT_EXISTS, T2S(&def->owner), T2S_EX(&def->name));
            return OG_ERROR;
        }

        if (pl_drop_library_internal(session, &def->owner, &library) != OG_SUCCESS) {
            dls_unlatch(session, ddl_latch, NULL);
            dls_unlatch(session, &user->user_latch, NULL);

            return OG_ERROR;
        }
    }

    if (pl_init_library_desc(session, &library, def) != OG_SUCCESS) {
        dls_unlatch(session, ddl_latch, NULL);
        dls_unlatch(session, &user->user_latch, NULL);

        return OG_ERROR;
    }

    CM_SAVE_STACK(session->stack);
    cursor = knl_push_cursor(session);
    cursor->row = (row_head_t *)cursor->buf;
    cursor->is_valid = OG_TRUE;

    if (pl_write_syslibrary(session, cursor, &library) != OG_SUCCESS) {
        CM_RESTORE_STACK(session->stack);
        dls_unlatch(session, ddl_latch, NULL);
        dls_unlatch(session, &user->user_latch, NULL);
        return OG_ERROR;
    }

    knl_commit(session);
    CM_RESTORE_STACK(session->stack);
    dls_unlatch(session, ddl_latch, NULL);
    dls_unlatch(session, &user->user_latch, NULL);

    return OG_SUCCESS;
}

status_t pl_drop_library(knl_handle_t se, knl_drop_def_t *def)
{
    knl_session_t *session = (knl_session_t *)se;
    pl_library_t library;
    dc_user_t *user = NULL;
    status_t status;

    if (knl_ddl_enabled(session, OG_FALSE) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (dc_open_user(session, &def->owner, &user) != OG_SUCCESS) {
        cm_reset_error_user(ERR_USER_OBJECT_NOT_EXISTS, T2S(&def->owner), T2S_EX(&def->name), ERR_TYPE_LIBRARY);
        return OG_ERROR;
    }

    bool32 exists = OG_FALSE;
    dls_latch_x(session, &user->lib_latch, session->id, NULL);
    if (pl_find_library(se, user->desc.id, &def->name, &library, &exists) != OG_SUCCESS) {
        dls_unlatch(session, &user->lib_latch, NULL);
        return OG_ERROR;
    }

    if (!exists) {
        dls_unlatch(session, &user->lib_latch, NULL);
        if ((def->options & DROP_IF_EXISTS)) {
            return OG_SUCCESS;
        }

        OG_THROW_ERROR(ERR_USER_OBJECT_NOT_EXISTS, "library", T2S(&def->owner), T2S_EX(&def->name));
        return OG_ERROR;
    }

    status = pl_drop_library_internal(session, &def->name, &library);
    knl_commit(session);
    dls_unlatch(session, &user->lib_latch, NULL);

    return status;
}