* 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_debugger.c
*
*
* IDENTIFICATION
* src/ogsql/pl/debug/pl_debugger.c
*
* -------------------------------------------------------------------------
*/
#include "pl_debugger.h"
#include "pl_executor.h"
#include "srv_instance.h"
#include "pl_scalar.h"
#define PLD_IS_PL_TYPE(type) \
((type) == OGSQL_TYPE_ANONYMOUS_BLOCK || (type) == OGSQL_TYPE_CREATE_PROC || (type) == OGSQL_TYPE_CREATE_FUNC || \
(type) == OGSQL_TYPE_CREATE_TRIG)
static bool32 pld_is_proc_entry(const session_t *session, const pl_executor_t *exec)
{
sql_stmt_t *stmt = session->current_stmt;
if (exec->stack_base != 0) {
return OG_FALSE;
}
while (stmt->is_sub_stmt) {
stmt = (sql_stmt_t *)stmt->parent_stmt;
if (stmt->pl_context != NULL) {
return OG_FALSE;
}
}
return OG_TRUE;
}
* check current user is dba or not
*/
static bool32 pld_debug_user_is_dba(session_t *session, text_t *debug_user)
{
text_t role = {
.str = DBA_ROLE,
.len = 3
};
if (cm_text_str_equal_ins(debug_user, "SYS")) {
return OG_TRUE;
}
return knl_grant_role_with_option(&session->knl_session, debug_user, &role, OG_FALSE);
}
bool32 pld_has_privilege(session_t *session, text_t *debug_user, const void *exec)
{
debug_control_t *dbg_ctl = session->dbg_ctl;
text_t *user = NULL;
user = dbg_ctl->type == TARGET_SESSION ? &dbg_ctl->target_user : debug_user;
if (!cm_text_equal(&session->curr_user, user)) {
return OG_FALSE;
}
if (exec == NULL || ((pl_executor_t *)exec)->entity->def.user.len == 0) {
return OG_TRUE;
}
if (cm_text_equal(debug_user, &((pl_executor_t *)exec)->entity->def.user)) {
return OG_TRUE;
}
return pld_debug_user_is_dba(session, debug_user);
}
static void pld_try_init_callstack_info(session_t *session, debug_control_t *debug_ctl, bool32 *is_init)
{
pl_executor_t *exec = (pl_executor_t *)session->current_stmt->pl_exec;
dbg_callstack_info_t *callstack_info = debug_ctl->callstack_info;
sql_stmt_t *stmt = NULL;
sql_stmt_t *pl_stmts[PL_MAX_BLOCK_DEPTH] = { NULL };
*is_init = OG_FALSE;
if (debug_ctl->max_stack_id != 0) {
return;
}
*is_init = OG_TRUE;
stmt = session->current_stmt;
while (stmt != NULL) {
if (stmt->pl_context != NULL) {
pl_stmts[debug_ctl->max_stack_id] = stmt;
debug_ctl->max_stack_id++;
}
if (!stmt->is_sub_stmt) {
break;
}
stmt = (sql_stmt_t *)stmt->parent_stmt;
}
for (uint32 i = 0; i < debug_ctl->max_stack_id; i++) {
exec = (pl_executor_t *)pl_stmts[debug_ctl->max_stack_id - 1 - i]->pl_exec;
if (cm_text2str(&exec->entity->def.user, callstack_info[i].owner, OG_NAME_BUFFER_SIZE) != OG_SUCCESS) {
callstack_info[i].owner[0] = '\0';
callstack_info[i].owner_len = 0;
} else {
callstack_info[i].owner_len = exec->entity->def.user.len;
}
if (cm_text2str(&exec->entity->def.name, callstack_info[i].object, OG_NAME_BUFFER_SIZE) != OG_SUCCESS) {
callstack_info[i].object[0] = '\0';
callstack_info[i].object_len = 0;
} else {
callstack_info[i].object_len = exec->entity->def.name.len;
}
callstack_info[i].exec = (void *)exec;
callstack_info[i].stmt = pl_stmts[debug_ctl->max_stack_id - 1 - i];
}
}
static void pld_update_callstack_info(session_t *session, pl_executor_t *exec, debug_control_t *debug_ctl)
{
dbg_callstack_info_t *callstack_info = debug_ctl->callstack_info;
uint32 curr_stack_id;
if (callstack_info == NULL) {
return;
}
if (pld_is_proc_entry((session_t *)session, (pl_executor_t *)exec)) {
debug_ctl->max_stack_id = 0;
} else {
bool32 is_init = OG_TRUE;
pld_try_init_callstack_info(session, debug_ctl, &is_init);
if (is_init) {
return;
}
}
curr_stack_id = debug_ctl->max_stack_id;
if (cm_text2str(&exec->entity->def.user, callstack_info[curr_stack_id].owner, OG_NAME_BUFFER_SIZE) != OG_SUCCESS) {
callstack_info[curr_stack_id].owner[0] = '\0';
callstack_info[curr_stack_id].owner_len = 0;
} else {
callstack_info[curr_stack_id].owner_len = exec->entity->def.user.len;
}
if (cm_text2str(&exec->entity->def.name, callstack_info[curr_stack_id].object, OG_NAME_BUFFER_SIZE) != OG_SUCCESS) {
callstack_info[curr_stack_id].object[0] = '\0';
callstack_info[curr_stack_id].object_len = 0;
} else {
callstack_info[curr_stack_id].object_len = exec->entity->def.name.len;
}
callstack_info[curr_stack_id].exec = (void *)exec;
callstack_info[curr_stack_id].stmt = session->current_stmt;
debug_ctl->max_stack_id++;
}
static inline void pld_try_init_pl_entries(session_t *session, debug_control_t *debug_ctl)
{
if (debug_ctl->max_stack_id != 0 && debug_ctl->pl_ref_entry == NULL) {
debug_ctl->pl_ref_entry = session->current_stmt->pl_ref_entry;
}
}
static inline void pld_try_set_pl_entries_null(debug_control_t *debug_ctl)
{
if (debug_ctl->max_stack_id == 0 && debug_ctl->pl_ref_entry != NULL) {
debug_ctl->pl_ref_entry = NULL;
}
}
static bool32 pld_isin_package_subproc(session_t *session)
{
pl_executor_t *exec = NULL;
sql_stmt_t *stmt = session->current_stmt;
while (stmt != NULL) {
exec = (pl_executor_t *)stmt->pl_exec;
if (exec != NULL && exec->entity->pl_type == PL_PACKAGE_BODY) {
return OG_TRUE;
}
if (!stmt->is_sub_stmt) {
break;
}
stmt = stmt->parent_stmt;
}
return OG_FALSE;
}
static void pld_proc_start(void *session, void *exec, status_t *status)
{
debug_control_t *debug_ctl = ((session_t *)session)->dbg_ctl;
OG_RETVOID_IFTRUE(pld_isin_package_subproc(session));
cm_spin_lock_if_exists(&((session_t *)session)->dbg_ctl_lock, NULL);
cm_spin_lock_if_exists(debug_ctl->debug_lock, NULL);
do {
OG_BREAK_IF_TRUE(!debug_ctl->is_attached);
pld_update_callstack_info((session_t *)session, (pl_executor_t *)exec, debug_ctl);
pld_try_init_pl_entries((session_t *)session, debug_ctl);
OG_BREAK_IF_TRUE(!pld_has_privilege((session_t *)session, &debug_ctl->debug_user, (pl_executor_t *)exec));
if (pld_is_proc_entry((session_t *)session, (pl_executor_t *)exec)) {
debug_ctl->is_force_terminate = OG_FALSE;
debug_ctl->is_force_pause = OG_FALSE;
debug_ctl->status = DBG_PRE_WAIT;
break;
}
if (debug_ctl->brk_flag == BRK_ANY_CALL) {
debug_ctl->status = DBG_PRE_WAIT;
break;
}
} while (0);
cm_spin_unlock_if_exists(debug_ctl->debug_lock);
cm_spin_unlock_if_exists(&((session_t *)session)->dbg_ctl_lock);
}
static bool32 pld_is_brk_point_in_proc(knl_session_t *sess, pl_executor_t *exec, dbg_break_info_t *brk_info)
{
pl_entry_t *pl_entry = exec->entity->entry;
if (!brk_info->is_using || !brk_info->is_enabled || exec->curr_line->loc.line != brk_info->loc.line ||
pl_entry == NULL || pl_entry->desc.type != brk_info->pl_type || pl_entry->desc.org_scn != brk_info->scn ||
!cm_str_equal(pl_entry->desc.name, brk_info->object)) {
return OG_FALSE;
}
dc_user_t *dc_user = NULL;
if (dc_open_user_by_id(sess, pl_entry->desc.uid, &dc_user) != OG_SUCCESS) {
cm_reset_error();
return OG_FALSE;
}
return cm_str_equal(dc_user->desc.name, brk_info->owner);
}
static bool32 pld_is_breakpoint_match(knl_session_t *sess, pl_executor_t *exec, debug_control_t *dbg_ctl)
{
dbg_break_info_t *break_info = dbg_ctl->brk_info;
bool32 is_match = OG_FALSE;
for (uint32 i = 0; i < dbg_ctl->max_break_id; i++) {
if (!pld_is_brk_point_in_proc(sess, exec, &break_info[i])) {
continue;
}
if (break_info[i].skipped_times < break_info[i].max_skip_times) {
break_info[i].skipped_times++;
continue;
}
break_info[i].skipped_times = 0;
is_match = OG_TRUE;
break;
}
return is_match;
}
static void pld_stmt_start(void *session, void *exec, status_t *status)
{
debug_control_t *debug_ctl = ((session_t *)session)->dbg_ctl;
OG_RETVOID_IFTRUE(pld_isin_package_subproc(session));
cm_spin_lock_if_exists(&((session_t *)session)->dbg_ctl_lock, NULL);
cm_spin_lock_if_exists(debug_ctl->debug_lock, NULL);
pl_executor_t *pl_exec = (pl_executor_t *)exec;
bool32 is_init = OG_TRUE;
*status = OG_SUCCESS;
do {
OG_BREAK_IF_TRUE(!debug_ctl->is_attached);
pld_try_init_callstack_info((session_t *)session, debug_ctl, &is_init);
pld_try_init_pl_entries((session_t *)session, debug_ctl);
debug_ctl->stmts = &((session_t *)session)->stmts;
OG_BREAK_IF_TRUE(!pld_has_privilege((session_t *)session, &debug_ctl->debug_user, (pl_executor_t *)exec));
if (pld_is_breakpoint_match((knl_session_t *)session, pl_exec, debug_ctl)) {
debug_ctl->status = DBG_PRE_WAIT;
}
while (((debug_ctl->status == DBG_WAITING) || (debug_ctl->status == DBG_PRE_WAIT)) &&
(debug_ctl->curr_count < debug_ctl->timeout)) {
if (((session_t *)session)->knl_session.canceled || ((session_t *)session)->knl_session.killed) {
OG_THROW_ERROR(ERR_OPERATION_CANCELED);
*status = OG_ERROR;
break;
}
debug_ctl->status = DBG_WAITING;
cm_spin_unlock_if_exists(debug_ctl->debug_lock);
cm_spin_unlock_if_exists(&((session_t *)session)->dbg_ctl_lock);
cm_sleep(100);
cm_spin_lock_if_exists(&((session_t *)session)->dbg_ctl_lock, NULL);
cm_spin_lock_if_exists(debug_ctl->debug_lock, NULL);
debug_ctl->curr_count++;
}
OG_BREAK_IF_TRUE(*status);
if ((debug_ctl->is_force_terminate) || (debug_ctl->brk_flag == BRK_ABORT)) {
debug_ctl->is_force_terminate = OG_FALSE;
debug_ctl->status = DBG_IDLE;
*status = OG_ERROR;
break;
}
} while (0);
cm_spin_unlock_if_exists(debug_ctl->debug_lock);
cm_spin_unlock_if_exists(&((session_t *)session)->dbg_ctl_lock);
}
static bool32 pld_is_exception_handling(const pl_executor_t *exec)
{
pl_line_ctrl_t *except = exec->body->except;
if (except == NULL) {
return OG_FALSE;
} else if (except->loc.line < exec->curr_line->loc.line) {
return OG_TRUE;
}
return OG_FALSE;
}
static inline void pld_stmt_end_brk_flag_check(const session_t *session, const pl_executor_t *exec, status_t *status)
{
debug_control_t *debug_ctl = session->dbg_ctl;
pl_line_ctrl_t *curr_line = exec->curr_line;
switch (debug_ctl->brk_flag) {
case BRK_NEXT_LINE:
if ((debug_ctl->brk_flag_stack_id == debug_ctl->max_stack_id) &&
((curr_line->next == NULL) || (curr_line->loc.line != curr_line->next->loc.line))) {
debug_ctl->status = DBG_PRE_WAIT;
}
break;
case BRK_ANY_CALL:
debug_ctl->status = DBG_PRE_WAIT;
break;
case BRK_EXCEPTION:
if (*status == OG_ERROR) {
debug_ctl->status = DBG_PRE_WAIT;
}
break;
case BRK_HANDLER:
if ((curr_line->type == LINE_END_EXCEPTION) ||
(curr_line->type == LINE_RETURN && pld_is_exception_handling(exec))) {
debug_ctl->status = DBG_PRE_WAIT;
}
break;
default:
break;
}
}
static void pld_stmt_end(void *session, void *exec, status_t *status)
{
debug_control_t *debug_ctl = ((session_t *)session)->dbg_ctl;
OG_RETVOID_IFTRUE(pld_isin_package_subproc(session));
cm_spin_lock_if_exists(&((session_t *)session)->dbg_ctl_lock, NULL);
cm_spin_lock_if_exists(debug_ctl->debug_lock, NULL);
bool32 is_init = OG_TRUE;
do {
OG_BREAK_IF_TRUE(!debug_ctl->is_attached);
pld_try_init_callstack_info((session_t *)session, debug_ctl, &is_init);
pld_try_init_pl_entries((session_t *)session, debug_ctl);
OG_BREAK_IF_TRUE(!pld_has_privilege((session_t *)session, &debug_ctl->debug_user, (pl_executor_t *)exec));
if (debug_ctl->is_force_pause) {
debug_ctl->is_force_pause = OG_FALSE;
debug_ctl->status = DBG_PRE_WAIT;
break;
}
pld_stmt_end_brk_flag_check((session_t *)session, (pl_executor_t *)exec, status);
} while (0);
cm_spin_unlock_if_exists(debug_ctl->debug_lock);
cm_spin_unlock_if_exists(&((session_t *)session)->dbg_ctl_lock);
}
static void pld_proc_end(void *session, void *exec, status_t *status)
{
debug_control_t *debug_ctl = ((session_t *)session)->dbg_ctl;
OG_RETVOID_IFTRUE(pld_isin_package_subproc(session));
cm_spin_lock_if_exists(&((session_t *)session)->dbg_ctl_lock, NULL);
cm_spin_lock_if_exists(debug_ctl->debug_lock, NULL);
bool32 is_init = OG_TRUE;
do {
OG_BREAK_IF_TRUE(!debug_ctl->is_attached);
pld_try_init_callstack_info((session_t *)session, debug_ctl, &is_init);
pld_try_init_pl_entries((session_t *)session, debug_ctl);
OG_BREAK_IF_TRUE(!pld_has_privilege((session_t *)session, &debug_ctl->debug_user, (pl_executor_t *)exec));
switch (debug_ctl->brk_flag) {
case BRK_RETURN:
debug_ctl->status = DBG_PRE_WAIT;
break;
case BRK_ANY_RETURN:
if (debug_ctl->brk_flag_stack_id == debug_ctl->max_stack_id) {
debug_ctl->status = DBG_PRE_WAIT;
}
break;
default:
break;
}
} while (0);
if (debug_ctl->max_stack_id <= 1) {
debug_ctl->status = DBG_IDLE;
} else {
debug_ctl->max_stack_id--;
}
pld_try_set_pl_entries_null(debug_ctl);
cm_spin_unlock_if_exists(debug_ctl->debug_lock);
cm_spin_unlock_if_exists(&((session_t *)session)->dbg_ctl_lock);
}
status_t pld_parse_exec_info(const debug_control_t *debug_ctl, pld_exec_info_t *exec_info)
{
pl_executor_t *exec = (pl_executor_t *)debug_ctl->callstack_info[exec_info->stack_id - 1].exec;
exec_info->curr_line = exec->curr_line;
return OG_SUCCESS;
}
status_t pld_parse_block_info(const debug_control_t *debug_ctl, pld_block_info_t *block_info)
{
pl_executor_t *exec = (pl_executor_t *)debug_ctl->callstack_info[block_info->stack_id - 1].exec;
pl_executor_t *next_exec = NULL;
block_info->curr_stack_start = exec->stack_base;
if (block_info->stack_id == debug_ctl->max_stack_id) {
block_info->next_stack_start = exec->block_stack.depth;
} else {
next_exec = (pl_executor_t *)debug_ctl->callstack_info[block_info->stack_id].exec;
if (exec != next_exec) {
block_info->next_stack_start = exec->block_stack.depth;
} else {
block_info->next_stack_start = next_exec->stack_base;
}
}
if (block_info->next_stack_start >= block_info->max_depth) {
OG_THROW_ERROR(ERR_PL_INDEX_ID_OVERFLOW, block_info->next_stack_start, "block_id", block_info->max_depth);
return OG_ERROR;
}
for (uint32 i = block_info->curr_stack_start; i < block_info->next_stack_start; i++) {
block_info->var_count[i] = exec->block_stack.items[i]->var_map.count;
}
return OG_SUCCESS;
}
static void pld_get_block_name(const pl_executor_t *exec, uint32 block_id, text_t *block_name)
{
pl_line_ctrl_t *line_ctrl = exec->block_stack.items[block_id]->entry;
text_t *name = NULL;
if (line_ctrl->type == LINE_FOR) {
name = ((pl_line_for_t *)line_ctrl)->name;
} else if (line_ctrl->type == LINE_BEGIN) {
name = ((pl_line_begin_t *)line_ctrl)->name;
}
*block_name = (name != NULL) ? *name : CM_NULL_TEXT;
}
static inline sql_stmt_t *pld_ref_cursor_get(const debug_control_t *debug_ctl, const pl_cursor_slot_t *ref_cursor)
{
if (ref_cursor == NULL || ref_cursor->state == CUR_RES_FREE) {
return NULL;
}
return (ref_cursor->stmt_id == OG_INVALID_ID16) ? NULL :
(sql_stmt_t *)cm_list_get(debug_ctl->stmts, ref_cursor->stmt_id);
}
static void pld_prepare_cursor_info(debug_control_t *debug_ctl, variant_t *value, pld_cursor_info_t *cur_info)
{
pl_cursor_slot_t *ref_cursor = (pl_cursor_slot_t *)value->v_cursor.ref_cursor;
sql_stmt_t *cur_stmt = value->is_null ? NULL : pld_ref_cursor_get(debug_ctl, ref_cursor);
if (cur_stmt == NULL) {
cur_info->is_open = OG_FALSE;
cur_info->has_fetched = OG_FALSE;
cur_info->is_found = OG_FALSE;
cur_info->rows = 0;
} else {
cur_info->is_open = OG_TRUE;
cur_info->has_fetched = cur_stmt->cursor_info.has_fetched;
cur_info->is_found = !cur_stmt->eof;
cur_info->rows = cur_stmt->total_rows;
}
}
static uint32 pld_get_record_total_count(sql_stmt_t *stmt, sql_stmt_t *vm_stmt, const variant_t *val, uint32 *count)
{
plv_record_t *record = (plv_record_t *)val->v_record.record_meta;
uint32 curr_count = *count;
plv_record_attr_t *attr = NULL;
variant_t result;
pvm_context_t vm_context = GET_VM_CTX(vm_stmt);
OPEN_VM_PTR(&val->v_record.value, vm_context);
udt_mtrl_record_head_t *record_head = NULL;
record_head = (udt_mtrl_record_head_t *)d_ptr;
for (uint32 i = 0; i < record->count; i++) {
attr = udt_seek_field_by_id(record, i);
curr_count++;
if ((attr->type == UDT_RECORD || attr->type == UDT_OBJECT) &&
IS_VALID_MTRL_ROWID(record_head->field[i].rowid)) {
if (pld_record_field_read(stmt, vm_stmt, attr, &record_head->field[i], &result) != OG_SUCCESS) {
CLOSE_VM_PTR_EX(&val->v_record.value, vm_context);
return OG_ERROR;
}
if (pld_get_record_total_count(stmt, vm_stmt, &result, &curr_count) != OG_SUCCESS) {
CLOSE_VM_PTR_EX(&val->v_record.value, vm_context);
return OG_ERROR;
}
}
}
CLOSE_VM_PTR(&val->v_record.value, vm_context);
*count = curr_count;
return OG_SUCCESS;
}
static status_t pld_get_record_total_value(sql_stmt_t *stmt, sql_stmt_t *vm_stmt, const variant_t *value,
const text_t *parent_name, pld_var_info_t *pld_var_info, uint32 *index)
{
udt_mtrl_record_head_t *record_head = NULL;
uint32 curr_index = *index;
plv_record_attr_t *attr = NULL;
variant_t result;
pvm_context_t vm_context = GET_VM_CTX(vm_stmt);
status_t status = OG_SUCCESS;
OPEN_VM_PTR(&value->v_record.value, vm_context);
record_head = (udt_mtrl_record_head_t *)d_ptr;
for (uint32 i = 0; i < record_head->count; i++) {
attr = udt_seek_field_by_id((plv_record_t *)value->v_record.record_meta, i);
pld_var_info->total_field[curr_index] = record_head->field[i];
pld_var_info->total_attr[curr_index] = attr;
pld_var_info->total_parent_name[curr_index] = *parent_name;
curr_index++;
if ((attr->type == UDT_RECORD || attr->type == UDT_OBJECT) &&
IS_VALID_MTRL_ROWID(record_head->field[i].rowid)) {
if (pld_record_field_read(stmt, vm_stmt, attr, &record_head->field[i], &result) != OG_SUCCESS) {
status = OG_ERROR;
break;
}
if (pld_get_record_total_value(stmt, vm_stmt, &result, &attr->name, pld_var_info, &curr_index) !=
OG_SUCCESS) {
status = OG_ERROR;
break;
}
}
}
CLOSE_VM_PTR(&value->v_record.value, vm_context);
*index = curr_index;
return status;
}
static status_t pld_prepare_record_attr(sql_stmt_t *stmt, debug_control_t *debug_ctl, pld_var_info_t *pld_var_info,
const ple_var_t *var, bool32 *eof)
{
sql_stmt_t *curr_stmt = (sql_stmt_t *)debug_ctl->callstack_info[pld_var_info->stack_id - 1].stmt;
uint32 index = 0;
errno_t err;
uint32 count = 0;
OG_RETURN_IFERR(pld_get_record_total_count(stmt, curr_stmt, &var->value, &count));
if (pld_var_info->m_offset > count) {
OG_THROW_ERROR(ERR_PL_INDEX_ID_OVERFLOW, pld_var_info->m_offset, "m_offset", count);
return OG_ERROR;
}
if (pld_var_info->total_field == NULL) {
OG_RETURN_IFERR(sql_push(stmt, count * sizeof(udt_mtrl_record_field_t), (void **)&pld_var_info->total_field));
err = memset_sp(pld_var_info->total_field, count * sizeof(udt_mtrl_record_field_t), 0xFF,
count * sizeof(udt_mtrl_record_field_t));
if (err != EOK) {
OG_THROW_ERROR(ERR_SYSTEM_CALL, err);
return OG_ERROR;
}
OG_RETURN_IFERR(sql_push(stmt, count * sizeof(pointer_t), (void **)&pld_var_info->total_attr));
err = memset_sp(pld_var_info->total_attr, count * sizeof(pointer_t), 0, count * sizeof(pointer_t));
if (err != EOK) {
OG_THROW_ERROR(ERR_SYSTEM_CALL, err);
return OG_ERROR;
}
OG_RETURN_IFERR(sql_push(stmt, count * sizeof(text_t), (void **)&pld_var_info->total_parent_name));
err = memset_sp(pld_var_info->total_parent_name, count * sizeof(text_t), 0, count * sizeof(text_t));
if (err != EOK) {
OG_THROW_ERROR(ERR_SYSTEM_CALL, err);
return OG_ERROR;
}
OG_RETURN_IFERR(pld_get_record_total_value(stmt, curr_stmt, &var->value, &var->decl->name, pld_var_info,
&index));
}
pld_var_info->is_attr_in_vm = OG_TRUE;
pld_var_info->is_obj = OG_FALSE;
pld_var_info->name = pld_var_info->total_attr[pld_var_info->m_offset - 1]->name;
pld_var_info->parent_name = pld_var_info->total_parent_name[pld_var_info->m_offset - 1];
pld_var_info->field = pld_var_info->total_field[pld_var_info->m_offset - 1];
pld_var_info->attr = pld_var_info->total_attr[pld_var_info->m_offset - 1];
pld_var_info->curr_stmt = curr_stmt;
if (pld_var_info->m_offset == count) {
*eof = OG_TRUE;
} else {
*eof = OG_FALSE;
}
return OG_SUCCESS;
}
static uint32 pld_get_object_total_count(sql_stmt_t *stmt, sql_stmt_t *vm_stmt, const variant_t *value, uint32 *count)
{
plv_object_t *object = (plv_object_t *)value->v_object.object_meta;
uint32 curr_count = *count;
plv_object_attr_t *attr = NULL;
variant_t result;
pvm_context_t vm_context = GET_VM_CTX(vm_stmt);
if (IS_INVALID_MTRL_ROWID(value->v_object.value)) {
return OG_SUCCESS;
}
OPEN_VM_PTR(&value->v_object.value, vm_context);
udt_mtrl_object_head_t *object_head = NULL;
object_head = (udt_mtrl_object_head_t *)d_ptr;
for (uint32 i = 0; i < object->count; i++) {
attr = udt_seek_obj_field_byid(object, i);
curr_count++;
if (attr->type == UDT_OBJECT && IS_VALID_MTRL_ROWID(object_head->field[i].rowid)) {
if (pld_object_field_read(stmt, vm_stmt, attr, &object_head->field[i], &result) != OG_SUCCESS) {
CLOSE_VM_PTR_EX(&value->v_object.value, vm_context);
return OG_ERROR;
}
if (pld_get_object_total_count(stmt, vm_stmt, &result, &curr_count) != OG_SUCCESS) {
CLOSE_VM_PTR_EX(&value->v_object.value, vm_context);
return OG_ERROR;
}
}
}
CLOSE_VM_PTR(&value->v_object.value, vm_context);
*count = curr_count;
return OG_SUCCESS;
}
static status_t pld_get_object_total_value(sql_stmt_t *stmt, sql_stmt_t *vm_stmt, const variant_t *value,
const text_t *parent_name, pld_var_info_t *pld_var_info, uint32 *index)
{
udt_mtrl_object_head_t *object_head = NULL;
uint32 curr_index = *index;
plv_object_attr_t *attr = NULL;
variant_t result;
pvm_context_t vm_context = GET_VM_CTX(vm_stmt);
status_t status = OG_SUCCESS;
OPEN_VM_PTR(&value->v_object.value, vm_context);
object_head = (udt_mtrl_object_head_t *)d_ptr;
for (uint32 i = 0; i < object_head->count; i++) {
attr = udt_seek_obj_field_byid((plv_object_t *)value->v_object.object_meta, i);
pld_var_info->obj_total_field[curr_index] = object_head->field[i];
pld_var_info->obj_total_attr[curr_index] = attr;
pld_var_info->total_parent_name[curr_index] = *parent_name;
curr_index++;
if (attr->type == UDT_OBJECT && IS_VALID_MTRL_ROWID(object_head->field[i].rowid)) {
if (pld_object_field_read(stmt, vm_stmt, attr, &object_head->field[i], &result) != OG_SUCCESS) {
status = OG_ERROR;
break;
}
if (pld_get_object_total_value(stmt, vm_stmt, &result, &attr->name, pld_var_info, &curr_index) !=
OG_SUCCESS) {
status = OG_ERROR;
break;
}
}
}
CLOSE_VM_PTR(&value->v_object.value, vm_context);
*index = curr_index;
return status;
}
static status_t pld_prepare_object_attr(sql_stmt_t *stmt, debug_control_t *debug_ctl, pld_var_info_t *pld_var_info,
const ple_var_t *var, bool32 *eof)
{
sql_stmt_t *curr_stmt = (sql_stmt_t *)debug_ctl->callstack_info[pld_var_info->stack_id - 1].stmt;
uint32 index = 0;
errno_t err;
uint32 count = 0;
OG_RETURN_IFERR(pld_get_object_total_count(stmt, curr_stmt, &var->value, &count));
if (pld_var_info->m_offset > count) {
OG_THROW_ERROR(ERR_PL_INDEX_ID_OVERFLOW, pld_var_info->m_offset, "m_offset", count);
return OG_ERROR;
}
if (pld_var_info->obj_total_field == NULL) {
OG_RETURN_IFERR(sql_push(stmt, count * sizeof(udt_mtrl_object_field_t), (void
**)&pld_var_info->obj_total_field));
err = memset_sp(pld_var_info->obj_total_field, count * sizeof(udt_mtrl_object_field_t), 0xFF,
count * sizeof(udt_mtrl_object_field_t));
if (err != EOK) {
OG_THROW_ERROR(ERR_SYSTEM_CALL, err);
return OG_ERROR;
}
OG_RETURN_IFERR(sql_push(stmt, count * sizeof(pointer_t), (void **)&pld_var_info->obj_total_attr));
err = memset_sp(pld_var_info->obj_total_attr, count * sizeof(pointer_t), 0, count * sizeof(pointer_t));
if (err != EOK) {
OG_THROW_ERROR(ERR_SYSTEM_CALL, err);
return OG_ERROR;
}
OG_RETURN_IFERR(sql_push(stmt, count * sizeof(text_t), (void **)&pld_var_info->total_parent_name));
err = memset_sp(pld_var_info->total_parent_name, count * sizeof(text_t), 0, count * sizeof(text_t));
if (err != EOK) {
OG_THROW_ERROR(ERR_SYSTEM_CALL, err);
return OG_ERROR;
}
OG_RETURN_IFERR(pld_get_object_total_value(stmt, curr_stmt, &var->value, &var->decl->name, pld_var_info,
&index));
}
pld_var_info->is_attr_in_vm = OG_TRUE;
pld_var_info->is_obj = OG_TRUE;
pld_var_info->name = pld_var_info->obj_total_attr[pld_var_info->m_offset - 1]->name;
pld_var_info->parent_name = pld_var_info->total_parent_name[pld_var_info->m_offset - 1];
pld_var_info->obj_field = pld_var_info->obj_total_field[pld_var_info->m_offset - 1];
pld_var_info->obj_attr = pld_var_info->obj_total_attr[pld_var_info->m_offset - 1];
pld_var_info->obj_curr_stmt = curr_stmt;
*eof = (pld_var_info->m_offset == count) ? OG_TRUE : OG_FALSE;
return OG_SUCCESS;
}
static status_t pld_check_var_info(pl_executor_t *exec, pld_var_info_t *pld_var_info, uint32 block_id)
{
if (block_id >= exec->block_stack.depth) {
OG_THROW_ERROR(ERR_PL_INDEX_ID_OVERFLOW, block_id, "block_id", exec->block_stack.depth);
return OG_ERROR;
}
if (pld_var_info->id >= exec->block_stack.items[block_id]->var_map.count) {
OG_THROW_ERROR(ERR_PL_INDEX_ID_OVERFLOW, pld_var_info->id, "id", exec->block_stack.items[block_id]->var_map.count);
return OG_ERROR;
}
return OG_SUCCESS;
}
status_t pld_get_var_info(sql_stmt_t *stmt, debug_control_t *debug_ctl, pld_var_info_t *pld_var_info, bool32 *is_found,
bool32 *eof)
{
pl_executor_t *exec = (pl_executor_t *)debug_ctl->callstack_info[pld_var_info->stack_id - 1].exec;
uint32 block_id = exec->stack_base + pld_var_info->block_id;
ple_var_t *var = NULL;
OG_RETURN_IFERR(pld_check_var_info(exec, pld_var_info, block_id));
pld_get_block_name(exec, block_id, &pld_var_info->block_name);
var = exec->block_stack.items[block_id]->var_map.items[pld_var_info->id];
if (var == NULL) {
*is_found = OG_FALSE;
return OG_SUCCESS;
}
*is_found = OG_TRUE;
pld_var_info->name = var->decl->name;
var_copy(&var->value, &pld_var_info->get_value);
pld_var_info->set_value = &var->value;
pld_var_info->type = var->exec_type;
pld_var_info->is_attr_in_vm = OG_FALSE;
pld_var_info->is_obj = OG_FALSE;
if (var->value.type == OG_TYPE_CURSOR) {
pld_prepare_cursor_info(debug_ctl, &var->value, &pld_var_info->cur_info);
}
if (var->decl->type & PLV_RECORD) {
if (pld_var_info->m_offset == PLD_INVALID_M_OFFSET) {
pld_var_info->get_value.is_null = OG_TRUE;
pld_var_info->type.datatype = OG_TYPE_RECORD;
pld_var_info->parent_name.str = NULL;
pld_var_info->parent_name.len = 0;
*eof = IS_INVALID_MTRL_ROWID(var->value.v_record.value) ? OG_TRUE : OG_FALSE;
return OG_SUCCESS;
}
return pld_prepare_record_attr(stmt, debug_ctl, pld_var_info, var, eof);
} else if (var->decl->type & PLV_OBJECT) {
if (pld_var_info->m_offset == PLD_INVALID_M_OFFSET) {
pld_var_info->get_value.is_null = OG_TRUE;
pld_var_info->type.datatype = OG_TYPE_OBJECT;
pld_var_info->parent_name.str = NULL;
pld_var_info->parent_name.len = 0;
*eof = IS_INVALID_MTRL_ROWID(var->value.v_object.value) ? OG_TRUE : OG_FALSE;
return OG_SUCCESS;
}
return pld_prepare_object_attr(stmt, debug_ctl, pld_var_info, var, eof);
}
pld_var_info->parent_name.str = NULL;
pld_var_info->parent_name.len = 0;
*eof = OG_TRUE;
return OG_SUCCESS;
}
status_t pld_get_cursor_buf(char *cursor_buf, uint32 buf_len, uint32 *using_len, const pld_cursor_info_t *cursor_info)
{
int iret_snprintf = 0;
if (!cursor_info->is_open) {
iret_snprintf = snprintf_s(cursor_buf, buf_len, buf_len - 1, "");
if (SECUREC_UNLIKELY(iret_snprintf == -1)) {
OG_THROW_ERROR(ERR_SYSTEM_CALL, iret_snprintf);
return OG_ERROR;
}
} else if (!cursor_info->has_fetched) {
iret_snprintf = snprintf_s(cursor_buf, buf_len, buf_len - 1, "open");
if (SECUREC_UNLIKELY(iret_snprintf == -1)) {
OG_THROW_ERROR(ERR_SYSTEM_CALL, iret_snprintf);
return OG_ERROR;
}
} else if (cursor_info->is_found) {
iret_snprintf = snprintf_s(cursor_buf, buf_len, buf_len - 1, "rowcount=%u, open, found", cursor_info->rows);
if (SECUREC_UNLIKELY(iret_snprintf == -1)) {
OG_THROW_ERROR(ERR_SYSTEM_CALL, iret_snprintf);
return OG_ERROR;
}
} else {
iret_snprintf = snprintf_s(cursor_buf, buf_len, buf_len - 1, "rowcount=0, open, notfound");
}
PRTS_RETURN_IFERR(iret_snprintf);
if (using_len != NULL) {
*using_len = (uint32)iret_snprintf;
}
return OG_SUCCESS;
}
status_t pld_record_field_read(sql_stmt_t *stmt, sql_stmt_t *vm_stmt, plv_record_attr_t *attr,
udt_mtrl_record_field_t *field, variant_t *result)
{
variant_t src;
status_t status = OG_ERROR;
char *org_base = NULL;
in debugging session invoke address function, need to check stack safe, so running session agent stack_base
need switch to debugger session agent stack_base
*/
cm_switch_stack_base(&vm_stmt->session->agent->thread, stmt->session->agent->thread.stack_base, &org_base);
OGSQL_SAVE_STACK(vm_stmt);
do {
OG_BREAK_IF_ERROR(udt_record_field_addr_read(vm_stmt, attr, &src, field));
if (attr->type == UDT_SCALAR) {
OG_BREAK_IF_ERROR(udt_copy_scalar_element(stmt, attr->scalar_field->type_mode, &src, result));
} else {
var_copy(&src, result);
}
status = OG_SUCCESS;
} while (0);
OGSQL_RESTORE_STACK(vm_stmt);
cm_switch_stack_base(&vm_stmt->session->agent->thread, org_base, &org_base);
return status;
}
static status_t pld_record_field_rec_write(sql_stmt_t *stmt, pld_var_info_t *var_info, uint32 field_id,
variant_t *right, udt_mtrl_record_field_t *parent)
{
status_t status;
char *org_base = NULL;
udt_mtrl_record_head_t *mtrl_head = NULL;
pvm_context_t vm_context = GET_VM_CTX(var_info->curr_stmt);
cm_switch_stack_base(&var_info->curr_stmt->session->agent->thread, stmt->session->agent->thread.stack_base,
&org_base);
OGSQL_SAVE_STACK(var_info->curr_stmt);
OPEN_VM_PTR(&parent->rowid, vm_context);
mtrl_head = (udt_mtrl_record_head_t *)d_ptr;
status = udt_record_field_addr_write(var_info->curr_stmt, var_info->attr, &mtrl_head->field[field_id], right);
CLOSE_VM_PTR(&parent->rowid, vm_context);
OGSQL_RESTORE_STACK(var_info->curr_stmt);
cm_switch_stack_base(&var_info->curr_stmt->session->agent->thread, org_base, &org_base);
return status;
}
static status_t pld_record_field_obj_write(sql_stmt_t *stmt, pld_var_info_t *var_info, uint32 field_id,
variant_t *right, udt_mtrl_object_field_t *obj_parent)
{
status_t status;
char *org_base = NULL;
udt_mtrl_object_head_t *obj_head = NULL;
pvm_context_t vm_context = GET_VM_CTX(var_info->obj_curr_stmt);
cm_switch_stack_base(&var_info->obj_curr_stmt->session->agent->thread, stmt->session->agent->thread.stack_base,
&org_base);
OGSQL_SAVE_STACK(var_info->obj_curr_stmt);
OPEN_VM_PTR(&obj_parent->rowid, vm_context);
obj_head = (udt_mtrl_object_head_t *)d_ptr;
status =
udt_object_field_addr_write(var_info->obj_curr_stmt, var_info->obj_attr, &obj_head->field[field_id], right);
CLOSE_VM_PTR(&obj_parent->rowid, vm_context);
OGSQL_RESTORE_STACK(var_info->obj_curr_stmt);
cm_switch_stack_base(&var_info->obj_curr_stmt->session->agent->thread, org_base, &org_base);
return status;
}
status_t pld_record_field_write(sql_stmt_t *stmt, debug_control_t *debug_ctl, pld_var_info_t *var_info, variant_t
*right)
{
status_t status;
pld_var_info_t top_record_info;
udt_mtrl_record_field_t record_parent;
udt_mtrl_object_field_t obj_parent;
bool8 is_obj = OG_FALSE;
uint32 field_id;
for (uint32 i = var_info->m_offset; i >= PLD_INVALID_M_OFFSET; i--) {
if (i == PLD_INVALID_M_OFFSET) {
bool32 is_found = OG_FALSE;
bool32 eof = OG_FALSE;
top_record_info.stack_id = var_info->stack_id;
top_record_info.block_id = var_info->block_id;
top_record_info.id = var_info->id;
top_record_info.m_offset = PLD_INVALID_M_OFFSET;
top_record_info.total_field = NULL;
top_record_info.total_attr = NULL;
top_record_info.total_parent_name = NULL;
OG_RETURN_IFERR(pld_get_var_info(stmt, debug_ctl, &top_record_info, &is_found, &eof));
record_parent.rowid = top_record_info.get_value.v_record.value;
record_parent.type = OG_TYPE_RECORD;
field_id = (uint32)(var_info->m_offset - 1);
break;
}
if (var_info->total_attr[i - 1]->type == UDT_RECORD) {
record_parent = var_info->total_field[i - 1];
field_id = var_info->m_offset - 1 - i;
break;
}
if (var_info->obj_total_attr[i - 1]->type == UDT_OBJECT) {
obj_parent = var_info->obj_total_field[i - 1];
field_id = var_info->m_offset - 1 - i;
is_obj = OG_TRUE;
break;
}
}
in debugging session invoke address function, need to check stack safe, so running session agent stack_base
need switch to debugger session agent stack_base
*/
if (!is_obj) {
status = pld_record_field_rec_write(stmt, var_info, field_id, right, &record_parent);
} else {
status = pld_record_field_obj_write(stmt, var_info, field_id, right, &obj_parent);
}
return status;
}
status_t pld_object_field_read(sql_stmt_t *stmt, sql_stmt_t *vm_stmt, plv_object_attr_t *attr,
udt_mtrl_object_field_t *field, variant_t *result)
{
variant_t src;
status_t status = OG_ERROR;
char *org_base = NULL;
in debugging session invoke address function, need to check stack safe, so running session agent stack_base
need switch to debugger session agent stack_base
*/
cm_switch_stack_base(&vm_stmt->session->agent->thread, stmt->session->agent->thread.stack_base, &org_base);
OGSQL_SAVE_STACK(vm_stmt);
do {
OG_BREAK_IF_ERROR(udt_object_field_addr_read(vm_stmt, attr, &src, field));
if (attr->type == UDT_SCALAR) {
OG_BREAK_IF_ERROR(udt_copy_scalar_element(stmt, attr->scalar_field->type_mode, &src, result));
} else {
var_copy(&src, result);
}
status = OG_SUCCESS;
} while (0);
OGSQL_RESTORE_STACK(vm_stmt);
cm_switch_stack_base(&vm_stmt->session->agent->thread, org_base, &org_base);
return status;
}
status_t pld_object_field_write(sql_stmt_t *stmt, debug_control_t *debug_ctl,
pld_var_info_t *var_info, variant_t *right)
{
status_t status;
pld_var_info_t top_object_info;
udt_mtrl_object_field_t parent;
char *org_base = NULL;
pvm_context_t vm_context = GET_VM_CTX(var_info->obj_curr_stmt);
udt_mtrl_object_head_t *mtrl_head = NULL;
uint32 field_id;
for (uint32 i = var_info->m_offset; i >= PLD_INVALID_M_OFFSET; i--) {
if (i == PLD_INVALID_M_OFFSET) {
bool32 is_found = OG_FALSE;
bool32 eof = OG_FALSE;
top_object_info.stack_id = var_info->stack_id;
top_object_info.block_id = var_info->block_id;
top_object_info.id = var_info->id;
top_object_info.m_offset = PLD_INVALID_M_OFFSET;
top_object_info.obj_total_field = NULL;
top_object_info.obj_total_attr = NULL;
top_object_info.total_parent_name = NULL;
OG_RETURN_IFERR(pld_get_var_info(stmt, debug_ctl, &top_object_info, &is_found, &eof));
parent.rowid = top_object_info.get_value.v_object.value;
parent.type = OG_TYPE_OBJECT;
field_id = (uint32)(var_info->m_offset - 1);
break;
}
if (var_info->obj_total_attr[i - 1]->type == UDT_OBJECT) {
parent = var_info->obj_total_field[i - 1];
field_id = var_info->m_offset - 1 - i;
break;
}
}
in debugging session invoke address function, need to check stack safe, so running session agent stack_base
need switch to debugger session agent stack_base
*/
cm_switch_stack_base(&var_info->obj_curr_stmt->session->agent->thread, stmt->session->agent->thread.stack_base,
&org_base);
OGSQL_SAVE_STACK(var_info->obj_curr_stmt);
OPEN_VM_PTR(&parent.rowid, vm_context);
mtrl_head = (udt_mtrl_object_head_t *)d_ptr;
status =
udt_object_field_addr_write(var_info->obj_curr_stmt, var_info->obj_attr, &mtrl_head->field[field_id], right);
CLOSE_VM_PTR(&parent.rowid, vm_context);
OGSQL_RESTORE_STACK(var_info->obj_curr_stmt);
cm_switch_stack_base(&var_info->obj_curr_stmt->session->agent->thread, org_base, &org_base);
return status;
}
status_t pld_set_var(sql_stmt_t *stmt, const pld_set_var_t *set_var)
{
return ple_copy_variant(stmt, set_var->src, set_var->dst, set_var->type);
}
status_t pld_open_proc_dc(sql_stmt_t *stmt, debug_control_t *debug_ctl, plm_find_pldesc_t *plm_find_pldesc,
pl_dc_t *pl_dc)
{
bool32 exist = OG_FALSE;
sql_stmt_t *target_stmt = NULL;
var_udo_t *obj = &plm_find_pldesc->v_udo;
pl_dc_t *dc = NULL;
source_location_t loc = { 1, 1 };
pl_dc_assist_t assist = { 0 };
for (uint32 i = 0; i < debug_ctl->max_stack_id; i++) {
target_stmt = debug_ctl->callstack_info[i].stmt;
dc = pl_get_regist_dc(target_stmt, obj, plm_find_pldesc->type);
if (dc != NULL) {
pl_dc_reopen(dc);
*pl_dc = *dc;
return OG_SUCCESS;
}
}
uint32 type = (uint32)plm_find_pldesc->type;
pl_dc_open_prepare_for_ignore_priv(&assist, stmt, &obj->user, &obj->name, type);
if (pl_dc_open(&assist, pl_dc, &exist) != OG_SUCCESS) {
return OG_ERROR;
}
if (!exist) {
return pl_unfound_error(stmt, obj, &loc, type);
}
return OG_SUCCESS;
}
void pld_register_debug_callbacks(dbg_callback_t *dbg_callback)
{
dbg_callback->proc_start = (dbg_callback_func_t)pld_proc_start;
dbg_callback->proc_end = (dbg_callback_func_t)pld_proc_end;
dbg_callback->stmt_start = (dbg_callback_func_t)pld_stmt_start;
dbg_callback->stmt_end = (dbg_callback_func_t)pld_stmt_end;
}
#define DECODE_PL_TYPE_NUM 8
text_t g_object_type_name[DECODE_PL_TYPE_NUM] = {
{ "PROCEDURE", 9 },
{ "FUNCTION", 8 },
{ "PACKAGE_SPEC", 12 },
{ "PACKAGE_BODY", 12 },
{ "TYPE_SPEC", 9 },
{ "TYPE_BODY", 9 },
{ "TRIGGER", 7 },
{ "", 0 },
};
status_t pld_get_pl_type(text_t *type, uint32 *pl_type)
{
if (cm_text_str_equal(type, "PROCEDURE")) {
*pl_type = PL_PROCEDURE;
return OG_SUCCESS;
}
if (cm_text_str_equal(type, "FUNCTION")) {
*pl_type = PL_FUNCTION;
return OG_SUCCESS;
}
if (cm_text_str_equal(type, "TRIGGER")) {
*pl_type = PL_TRIGGER;
return OG_SUCCESS;
}
OG_THROW_ERROR(ERR_INVALID_PARAMETER_ENUM, T2S(type), "need be [PROCEDURE|FUNCTION|TRIGGER]");
return OG_ERROR;
}
status_t pld_get_pl_type_text(uint32 temp_pl_type, text_t *type)
{
uint32 pl_type = temp_pl_type;
if (pl_type < PL_PROCEDURE) {
OG_THROW_ERROR(ERR_PARAMETER_TOO_SMALL, "pl_type", PL_PROCEDURE);
return OG_ERROR;
}
if (pl_type >= PL_ANONYMOUS_BLOCK) {
OG_THROW_ERROR(ERR_PARAMETER_TOO_LARGE, "pl_type", PL_TRIGGER);
return OG_ERROR;
}
uint32 i = 0;
while (pl_type != 0) {
pl_type = (pl_type >> 1);
i++;
}
*type = g_object_type_name[i - 1];
return OG_SUCCESS;
}
status_t pld_get_target_session_debug_info(sql_stmt_t *stmt, uint32 session_id, debug_control_t **debug_ctl,
spinlock_t **dbg_ctl_lock)
{
session_t *session = stmt->session;
if (!srv_get_debug_info(session_id, debug_ctl, dbg_ctl_lock) || (*debug_ctl)->type != TARGET_SESSION ||
session->knl_session.id != (*debug_ctl)->debug_id) {
OG_THROW_ERROR(ERR_DEBUG_SESSION_TYPE, "target session", session_id);
return OG_ERROR;
}
return OG_SUCCESS;
}
static bool32 text_is_dq_string(text_t *txt)
{
char c1;
char c2;
if (txt == NULL || txt->len < 2) {
return OG_FALSE;
}
c1 = txt->str[0];
c2 = txt->str[txt->len - 1];
if (c1 == c2 && (c1 == '\"' || c1 == '`')) {
return OG_TRUE;
}
return OG_FALSE;
}
void process_name_case_sensitive(text_t *name)
{
if (text_is_dq_string(name)) {
name->len = name->len - 2;
name->str = name->str + 1;
} else if (IS_CASE_INSENSITIVE) {
cm_text_upper(name);
}
}