Skip to content

File method_schema_info.cpp

File List > cubrid > src > method > method_schema_info.cpp

Go to the documentation of this file

/*
 *
 * Copyright 2016 CUBRID Corporation
 *
 *  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 "method_schema_info.hpp"

#include <algorithm>
#include <cstring>
#include <string>

#include "dbtype_def.h"
#include "dbi.h"
#include "method_query_handler.hpp"
#include "method_query_util.hpp"
#include "trigger_manager.h"
#include "work_space.h"
#include "schema_manager.h"

#include "language_support.h"
#include "deduplicate_key.h"
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

namespace cubmethod
{
  schema_info_handler::schema_info_handler (error_context &ctx)
    : m_error_ctx (ctx)
  {
    //
  }

  schema_info_handler::~schema_info_handler ()
  {
    close_and_free_session ();
  }

  schema_info
  schema_info_handler::get_schema_info (int schema_type, std::string &arg1, std::string &arg2, int flag)
  {
    int error = NO_ERROR;
    schema_info info;
    switch (schema_type)
      {
      case SCH_CLASS:
    error = sch_class_info (info, arg1, flag, 0);
    break;
      case SCH_VCLASS:
    error = sch_class_info (info, arg1, flag, 1);
    break;
      case SCH_QUERY_SPEC:
    error = sch_queryspec (info, arg1);
    break;
      case SCH_ATTRIBUTE:
    error = sch_attr_info (info, arg1, arg2, flag, 0);
    break;
      case SCH_CLASS_ATTRIBUTE:
    error = sch_attr_info (info, arg1, arg2, flag, 1);
    break;
      case SCH_METHOD:
    error = sch_method_info (info, arg1, 0);
    break;
      case SCH_CLASS_METHOD:
    error = sch_method_info (info, arg1, 1);
    break;
      case SCH_METHOD_FILE:
    error = sch_methfile_info (info, arg1);
    break;
      case SCH_SUPERCLASS:
    error = sch_superclass (info, arg1, 1);
    break;
      case SCH_SUBCLASS:
    error = sch_superclass (info, arg1, 0);
    break;
      case SCH_CONSTRAINT:
    error = sch_constraint (info, arg1);
    break;
      case SCH_TRIGGER:
    error = sch_trigger (info, arg1, flag);
    break;
      case SCH_CLASS_PRIVILEGE:
    error = sch_class_priv (info, arg1, flag);
    break;
      case SCH_ATTR_PRIVILEGE:
    error = sch_attr_priv (info, arg1, arg2, flag);
    break;
      case SCH_DIRECT_SUPER_CLASS:
    error = sch_direct_super_class (info, arg1, flag);
    break;
      case SCH_PRIMARY_KEY:
    error = sch_primary_key (info, arg1);
    break;
      case SCH_IMPORTED_KEYS:
    error = sch_imported_keys (info, arg1);
    break;
      case SCH_EXPORTED_KEYS:
    error = sch_exported_keys_or_cross_reference (info, arg1, arg2, false);
    break;
      case SCH_CROSS_REFERENCE:
    error = sch_exported_keys_or_cross_reference (info, arg1, arg2, true);
    break;
      default:
    error = ER_FAILED;
    break;
      }

    if (error == NO_ERROR)
      {
    // TODO: what should I do here?
    info.schema_type = schema_type;
      }
    else
      {
    close_and_free_session ();

    // TODO: proper error code
    m_error_ctx.set_error (METHOD_CALLBACK_ER_SCHEMA_TYPE, NULL, __FILE__, __LINE__);
      }

    return info;
  }

  int
  schema_info_handler::execute_schema_info_query (std::string &sql_stmt)
  {
    lang_set_parser_use_client_charset (false);

    db_init_lexer_lineno ();
    m_session = db_open_buffer (sql_stmt.c_str());
    if (!m_session)
      {
    lang_set_parser_use_client_charset (true);
    m_error_ctx.set_error (db_error_code (), db_error_string (1), __FILE__, __LINE__);
    return ER_FAILED;
      }

    int stmt_id = db_compile_statement (m_session);
    if (stmt_id < 0)
      {
    close_and_free_session ();
    m_error_ctx.set_error (stmt_id, NULL, __FILE__, __LINE__);
    return ER_FAILED;
      }

    DB_QUERY_RESULT *result = NULL;
    int stmt_type = db_get_statement_type (m_session, stmt_id);
    lang_set_parser_use_client_charset (false);
    int num_result = db_execute_statement (m_session, stmt_id, &result);
    lang_set_parser_use_client_charset (true);

    if (num_result < 0)
      {
    close_and_free_session ();
    m_error_ctx.set_error (stmt_id, NULL, __FILE__, __LINE__);
    return ER_FAILED;
      }

    query_result &q_result = m_q_result;
    q_result.stmt_type = stmt_type;
    q_result.stmt_id = stmt_id;
    q_result.tuple_count = num_result;
    q_result.result = result;
    q_result.include_oid = false;

    // TODO: managing structure for schema info queries
    /*
    m_q_result.push_back (q_result);
    m_max_col_size = -1;
    m_current_result_index = 0;
    m_current_result = &m_q_result[m_current_result_index];
    m_has_result_set = true;
    */

    return num_result;
  }

  int
  schema_info_handler::sch_class_info (schema_info &info, std::string &class_name,
                       int pattern_flag, int v_class_flag)
  {
    std::string schema_name;
    std::string class_name_only;
    std::size_t found;

    std::transform (class_name.begin(), class_name.end(), class_name.begin(), ::tolower);

    class_name_only = class_name;
    found = class_name.find ('.');
    if (found != std::string::npos)
      {
    /* If the length is not correct, the username is invalid, so compare the entire class_name. */
    if (found > 0 && found < DB_MAX_SCHEMA_LENGTH)
      {
        schema_name = class_name.substr (0, found);
        class_name_only = class_name.substr (found + 1);
      }
      }

    // *INDENT-OFF*
    std::string sql = ""
    "SELECT "
      "CASE "
        "WHEN is_system_class = 'NO' THEN LOWER (owner_name) || '.' || class_name "
        "ELSE class_name "
        "END AS unique_name, "
      "CAST ( "
          "CASE "
        "WHEN is_system_class = 'YES' THEN 0 "
        "WHEN class_type = 'CLASS' THEN 2 "
        "ELSE 1 "
        "END "
          "AS SHORT "
        "), "
      "comment "
    "FROM "
      "db_class "
    "WHERE 1 = 1 ";
    // *INDENT-ON*

    if (v_class_flag)
      {
    sql.append ("AND class_type = 'VCLASS' ");
      }

    if (pattern_flag & CLASS_NAME_PATTERN_MATCH)
      {
    if (!class_name_only.empty())
      {
        /* AND class_name LIKE '%s' ESCAPE '%s' */
        sql.append ("AND class_name LIKE '");
        sql.append (class_name_only);
        sql.append ("' ESCAPE '");
        sql.append (get_backslash_escape_string ());
        sql.append ("' ");
      }
      }
    else
      {
    /* AND class_name = '%s' */
    sql.append ("AND class_name = '");
    sql.append (class_name_only);
    sql.append ("' ");
      }

    if (!schema_name.empty())
      {
    /* AND owner_name = UPPER ('%s') */
    sql.append ("AND owner_name = UPPER ('");
    sql.append (schema_name);
    sql.append ("') ");
      }

    int num_result = execute_schema_info_query (sql);
    if (num_result < 0)
      {
    return num_result;
      }

    info.num_result = num_result;
    return NO_ERROR;
  }

  int
  schema_info_handler::sch_attr_info (schema_info &info, std::string &class_name,
                      std::string &attr_name, int pattern_flag,
                      int class_attr_flag)
  {
    std::string schema_name;
    std::string class_name_only;
    std::size_t found;

    std::transform (class_name.begin(), class_name.end(), class_name.begin(), ::tolower);
    std::transform (attr_name.begin(), attr_name.end(), attr_name.begin(), ::tolower);

    class_name_only = class_name;
    found = class_name.find ('.');
    if (found != std::string::npos)
      {
    /* If the length is not correct, the username is invalid, so compare the entire class_name. */
    if (found > 0 && found < DB_MAX_SCHEMA_LENGTH)
      {
        schema_name = class_name.substr (0, found);
        class_name_only = class_name.substr (found + 1);
      }
      }

    // *INDENT-OFF*
    std::string sql = ""
    "SELECT "
      "CASE "
        "WHEN ( "
        "SELECT b.is_system_class "
        "FROM db_class b "
        "WHERE b.class_name = a.class_name AND b.owner_name = a.owner_name "
          ") = 'NO' THEN LOWER (a.owner_name) || '.' || a.class_name "
        "ELSE a.class_name "
        "END AS unique_name, "
      "a.attr_name "
    "FROM "
      "db_attribute a "
    "WHERE 1 = 1 ";
    // *INDENT-ON*

    if (class_attr_flag)
      {
    sql.append ("AND a.attr_type = 'CLASS' ");
      }
    else
      {
    sql.append ("AND a.attr_type in {'INSTANCE', 'SHARED'} ");
      }

    if (pattern_flag & CLASS_NAME_PATTERN_MATCH)
      {
    if (!class_name_only.empty())
      {
        /* AND class_name LIKE '%s' ESCAPE '%s' */
        sql.append ("AND a.class_name LIKE '");
        sql.append (class_name_only);
        sql.append ("' ESCAPE '");
        sql.append (get_backslash_escape_string ());
        sql.append ("' ");
      }
      }
    else
      {
    /* AND class_name = '%s' */
    sql.append ("AND a.class_name = '");
    sql.append (class_name_only);
    sql.append ("' ");
      }

    if (pattern_flag & ATTR_NAME_PATTERN_MATCH)
      {
    if (!attr_name.empty())
      {
        /* AND a.attr_name LIKE '%s' ESCAPE '%s' */
        sql.append ("AND a.attr_name LIKE '");
        sql.append (attr_name);
        sql.append ("' ESCAPE '");
        sql.append (get_backslash_escape_string ());
        sql.append ("' ");
      }
      }
    else
      {
    /* AND a.attr_name = '%s' */
    sql.append ("AND a.class_name = '");
    sql.append (attr_name);
    sql.append ("' ");
      }

    if (!schema_name.empty())
      {
    /* AND owner_name = UPPER ('%s') */
    sql.append ("AND a.owner_name = UPPER ('");
    sql.append (schema_name);
    sql.append ("') ");
      }

    sql.append ("ORDER BY a.class_name, a.def_order ");

    int num_result = execute_schema_info_query (sql);
    if (num_result < 0)
      {
    return num_result;
      }

    info.num_result = num_result;
    return NO_ERROR;
  }

  int
  schema_info_handler::sch_queryspec (schema_info &info, std::string &class_name)
  {
    std::string schema_name;
    std::string class_name_only;
    std::size_t found;

    std::transform (class_name.begin(), class_name.end(), class_name.begin(), ::tolower);

    class_name_only = class_name;
    found = class_name.find ('.');
    if (found != std::string::npos)
      {
    /* If the length is not correct, the username is invalid, so compare the entire class_name. */
    if (found > 0 && found < DB_MAX_SCHEMA_LENGTH)
      {
        schema_name = class_name.substr (0, found);
        class_name_only = class_name.substr (found + 1);
      }
      }

    std::string sql = "SELECT vclass_def FROM db_vclass WHERE unique_name = '";
    sql.append (class_name_only);
    sql.append ("' ");

    if (!schema_name.empty())
      {
    /* AND owner_name = UPPER ('%s') */
    sql.append ("AND owner_name = UPPER ('");
    sql.append (schema_name);
    sql.append ("') ");
      }

    int num_result = execute_schema_info_query (sql);
    if (num_result < 0)
      {
    return num_result;
      }

    info.num_result = num_result;
    return NO_ERROR;
  }

  int
  schema_info_handler::sch_method_info (schema_info &info, std::string &class_name, int flag)
  {
    DB_OBJECT *class_obj = db_find_class (class_name.c_str());
    DB_METHOD *method_list;
    if (flag)
      {
    method_list = db_get_class_methods (class_obj);
      }
    else
      {
    method_list = db_get_methods (class_obj);
      }

    int num_method = 0;
    for (DB_METHOD *method = method_list; method; method = db_method_next (method))
      {
    num_method++;
      }

    info.num_result = num_method;
    m_method_list = method_list;
    return NO_ERROR;
  }

  int
  schema_info_handler::sch_methfile_info (schema_info &info, std::string &class_name)
  {
    DB_OBJECT *class_obj = db_find_class (class_name.c_str());
    DB_METHFILE *method_files = db_get_method_files (class_obj);

    int num_mf= 0;
    for (DB_METHFILE *mf = method_files; mf; mf = db_methfile_next (mf))
      {
    num_mf++;
      }
    info.num_result = num_mf;
    m_method_files = method_files;
    return NO_ERROR;
  }

  int
  schema_info_handler::sch_superclass (schema_info &info, std::string &class_name, int flag)
  {
    int error = NO_ERROR;
    DB_OBJECT *class_obj = db_find_class (class_name.c_str());
    DB_OBJLIST *obj_list = NULL;
    if (flag)
      {
    obj_list = db_get_superclasses (class_obj);
      }
    else
      {
    obj_list = db_get_subclasses (class_obj);
      }

    class_table ct;
    for (DB_OBJLIST *tmp = obj_list; tmp; tmp = tmp->next)
      {
    char *p = (char *) db_get_class_name (tmp->op);
    int cls_type = class_type (tmp->op);
    if (cls_type < 0)
      {
        error = cls_type;
        break;
      }

    ct.class_name.assign (p);
    ct.class_type = cls_type;
    m_obj_list.push_back (ct);
      }

    db_objlist_free (obj_list);
    info.num_result = m_obj_list.size ();
    return error;
  }

  int
  schema_info_handler::sch_constraint (schema_info &info, std::string &class_name)
  {
    DB_OBJECT *class_obj = db_find_class (class_name.c_str ());
    DB_CONSTRAINT *constraint = db_get_constraints (class_obj);
    int num_const = 0;
    for (DB_CONSTRAINT *tmp_c = constraint; tmp_c; tmp_c = db_constraint_next (tmp_c))
      {
    int type = db_constraint_type (tmp_c);
    switch (type)
      {
      case DB_CONSTRAINT_UNIQUE:
      case DB_CONSTRAINT_INDEX:
      case DB_CONSTRAINT_REVERSE_UNIQUE:
      case DB_CONSTRAINT_REVERSE_INDEX:
      {
        DB_ATTRIBUTE **attr = db_constraint_attributes (tmp_c);
        for (int i = 0; attr[i]; i++)
          {
        num_const++;
          }
      }
      default:
        break;
      }
      }
    info.num_result = num_const;
    m_constraint = constraint;
    return NO_ERROR;
  }

  int
  schema_info_handler::sch_trigger (schema_info &info, std::string &class_name, int flag)
  {
    int error = NO_ERROR;
    bool is_pattern_match = (flag & CLASS_NAME_PATTERN_MATCH) ? true : false;

    info.num_result = 0;
    if (class_name.empty () && !is_pattern_match)
      {
    return NO_ERROR;
      }

    DB_OBJLIST *tmp_trigger = NULL;
    if (db_find_all_triggers (&tmp_trigger) < 0)
      {
    return NO_ERROR;
      }

    int num_trig = 0;
    DB_OBJLIST *all_trigger = NULL;
    if (class_name.empty ())
      {
    all_trigger = tmp_trigger;
    num_trig = db_list_length ((DB_LIST *) all_trigger);
      }
    else
      {
    std::string schema_name;
    DB_OBJECT *owner = NULL;

    std::string class_name_only = class_name;
    std::size_t found = class_name.find ('.');
    if (found != std::string::npos)
      {
        /* If the length is not correct, the username is invalid, so compare the entire class_name. */
        if (found > 0 && found < DB_MAX_SCHEMA_LENGTH)
          {
        schema_name = class_name.substr (0, found);

        /* If the user does not exist, compare the entire class_name. */
        owner = db_find_user (schema_name.c_str ());
        if (owner != NULL)
          {
            class_name_only = class_name.substr (found + 1);
          }
          }
      }

    DB_OBJLIST *tmp = NULL;
    for (tmp = tmp_trigger; tmp; tmp = tmp->next)
      {
        MOP tmp_obj = tmp->op;
        assert (tmp_obj != NULL);

        TR_TRIGGER *trigger = tr_map_trigger (tmp_obj, 1);
        if (trigger == NULL)
          {
        assert (er_errid () != NO_ERROR);
        error = er_errid ();
        break;
          }

        DB_OBJECT *obj_trigger_target = trigger->class_mop;
        assert (obj_trigger_target != NULL);

        const char *name_trigger_target = sm_get_ch_name (obj_trigger_target);
        if (name_trigger_target == NULL)
          {
        assert (er_errid () != NO_ERROR);
        error = er_errid ();
        break;
          }

        const char *only_name_trigger_target = name_trigger_target;
        /* If the user does not exist, compare the entire class_name. */
        if (owner)
          {
        only_name_trigger_target = strchr (name_trigger_target, '.');
        if (only_name_trigger_target)
          {
            only_name_trigger_target = only_name_trigger_target + 1;
          }
        else
          {
            assert (false);
          }

        /* If the owner is different from the specified owner, skip it. */
        if (db_get_owner (tmp_obj) != owner)
          {
            continue;
          }
          }

        if (is_pattern_match)
          {
        if (str_like (std::string (name_trigger_target), class_name_only.c_str (), '\\') == 1)
          {
            error = ml_ext_add (&all_trigger, tmp_obj, NULL);
            if (error != NO_ERROR)
              {
            break;
              }
            num_trig++;
          }
          }
        else
          {
        if (strcmp (class_name_only.c_str (), name_trigger_target) == 0)
          {
            error = ml_ext_add (&all_trigger, tmp_obj, NULL);
            if (error != NO_ERROR)
              {
            break;
              }
            num_trig++;
          }
          }
      }

    if (tmp_trigger)
      {
        ml_ext_free (tmp_trigger);
      }

    if (error != NO_ERROR && all_trigger)
      {
        ml_ext_free (all_trigger);
        all_trigger = NULL;
        num_trig = 0;
      }
      }

    info.num_result = num_trig;
    m_all_trigger = all_trigger;
    return error;
  }

  int
  schema_info_handler::sch_class_priv (schema_info &info, std::string &class_name, int pat_flag)
  {
    int num_tuple = 0;
    unsigned int class_priv;

    if ((pat_flag & CLASS_NAME_PATTERN_MATCH) == 0)
      {
    if (!class_name.empty())
      {
        DB_OBJECT *class_obj = db_find_class (class_name.c_str ());
        if (class_obj != NULL)
          {
        if (db_get_class_privilege (class_obj, &class_priv) >= 0)
          {
            num_tuple = set_priv_table (m_priv_tbl, 0, (char *) db_get_class_name (class_obj), class_priv);
          }
          }
      }
      }
    else
      {
    std::string schema_name;
    DB_OBJECT *owner = NULL;

    std::string class_name_only = class_name;
    std::size_t found = class_name.find ('.');
    if (found != std::string::npos)
      {
        /* If the length is not correct, the username is invalid, so compare the entire class_name. */
        if (found > 0 && found < DB_MAX_SCHEMA_LENGTH)
          {
        schema_name = class_name.substr (0, found);

        /* If the user does not exist, compare the entire class_name. */
        owner = db_find_user (schema_name.c_str ());
        if (owner != NULL)
          {
            class_name_only = class_name.substr (found + 1);
          }
          }
      }

    DB_OBJLIST *obj_list = db_get_all_classes ();
    for (DB_OBJLIST *tmp = obj_list; tmp; tmp = tmp->next)
      {
        char *p = (char *) db_get_class_name (tmp->op);
        char *q = p;
        /* If the user does not exist, compare the entire class_name. */
        if (owner && db_is_system_class (tmp->op) == FALSE)
          {
        /* p: unique_name, q: class_name */
        q = strchr (p, '.');
        if (q)
          {
            q = q + 1;
          }
        else
          {
            assert (false);
          }

        /* If the owner is different from the specified owner, skip it. */
        if (db_get_owner (tmp->op) != owner)
          {
            continue;
          }
          }
        if (!class_name.empty() && str_like (std::string (q), class_name.c_str(), '\\') < 1)
          {
        continue;
          }

        if (db_get_class_privilege (tmp->op, &class_priv) >= 0)
          {
        num_tuple += set_priv_table (m_priv_tbl, num_tuple, (char *) db_get_class_name (tmp->op), class_priv);
          }
      }
    db_objlist_free (obj_list);
      }

    info.num_result = num_tuple;
    return NO_ERROR;
  }

  int
  schema_info_handler::sch_attr_priv (schema_info &info, std::string &class_name,
                      std::string &attr_name_pat,
                      int pat_flag)
  {
    int num_tuple = 0;

    DB_OBJECT *class_obj = db_find_class (class_name.c_str ());
    if (class_obj == NULL)
      {
    info.num_result = num_tuple;
    return NO_ERROR;
      }

    unsigned int class_priv;
    if (db_get_class_privilege (class_obj, &class_priv) >= 0)
      {
    DB_ATTRIBUTE *attributes = db_get_attributes (class_obj);
    for (DB_ATTRIBUTE *attr = attributes; attr; attr = db_attribute_next (attr))
      {
        char *attr_name = (char *) db_attribute_name (attr);
        if (pat_flag & ATTR_NAME_PATTERN_MATCH)
          {
        if (attr_name_pat.empty() == false && str_like (std::string (attr_name), attr_name_pat.c_str (), '\\') < 1)
          {
            continue;
          }
          }
        else
          {
        if (attr_name_pat.empty() == true || strcmp (attr_name, attr_name_pat.c_str ()) != 0)
          {
            continue;
          }
          }

        num_tuple += set_priv_table (m_priv_tbl, num_tuple, (char *) db_get_class_name (class_obj), class_priv);
      }
      }

    info.num_result = num_tuple;
    return NO_ERROR;
  }

  int
  schema_info_handler::sch_direct_super_class (schema_info &info, std::string &class_name,
      int pattern_flag)
  {
    std::string schema_name;
    std::string class_name_only;
    std::size_t found;

    std::transform (class_name.begin(), class_name.end(), class_name.begin(), ::tolower);

    class_name_only = class_name;
    found = class_name.find ('.');
    if (found != std::string::npos)
      {
    /* If the length is not correct, the username is invalid, so compare the entire class_name. */
    if (found > 0 && found < DB_MAX_SCHEMA_LENGTH)
      {
        schema_name = class_name.substr (0, found);
        class_name_only = class_name.substr (found + 1);
      }
      }

    // *INDENT-OFF*
    std::string sql = ""
    "SELECT "
      "CASE "
        "WHEN ( "
        "SELECT b.is_system_class "
        "FROM db_class b "
        "WHERE b.class_name = a.class_name AND b.owner_name = a.owner_name "
          ") = 'NO' THEN LOWER (a.owner_name) || '.' || a.class_name "
        "ELSE a.class_name "
        "END AS unique_name, "
      "CASE "
        "WHEN ( "
        "SELECT b.is_system_class "
        "FROM db_class b "
        "WHERE b.class_name = a.super_class_name AND b.owner_name = a.super_owner_name "
          ") = 'NO' THEN LOWER (a.super_owner_name) || '.' || a.super_class_name "
        "ELSE a.super_class_name "
        "END AS super_unique_name "
    "FROM "
      "db_direct_super_class a "
    "WHERE 1 = 1 ";
    // *INDENT-ON*

    if (pattern_flag & CLASS_NAME_PATTERN_MATCH)
      {
    if (!class_name_only.empty())
      {
        /* AND class_name LIKE '%s' ESCAPE '%s' */
        sql.append ("AND class_name LIKE '");
        sql.append (class_name_only);
        sql.append ("' ESCAPE '");
        sql.append (get_backslash_escape_string ());
        sql.append ("' ");
      }
      }
    else
      {
    /* AND class_name = '%s' */
    sql.append ("AND class_name = '");
    sql.append (class_name_only);
    sql.append ("' ");
      }

    if (!schema_name.empty())
      {
    /* AND owner_name = UPPER ('%s') */
    sql.append ("AND owner_name = UPPER ('");
    sql.append (schema_name);
    sql.append ("') ");
      }

    int num_result = execute_schema_info_query (sql);
    if (num_result < 0)
      {
    return num_result;
      }

    info.num_result = num_result;
    return NO_ERROR;
  }

  int schema_info_handler::sch_primary_key (schema_info &info, std::string &class_name)
  {
    std::string schema_name;
    std::string class_name_only;
    std::size_t found;
    int num_result = 0;
    int i;

    std::transform (class_name.begin(), class_name.end(), class_name.begin(), ::tolower);

    class_name_only = class_name;
    found = class_name.find ('.');
    if (found != std::string::npos)
      {
    /* If the length is not correct, the username is invalid, so compare the entire class_name. */
    if (found > 0 && found < DB_MAX_SCHEMA_LENGTH)
      {
        schema_name = class_name.substr (0, found);
        class_name_only = class_name.substr (found + 1);
      }
      }

    DB_OBJECT *class_object = db_find_class (class_name.c_str ());
    if (class_object != NULL)
      {
    // *INDENT-OFF*
    std::string sql = ""
        "SELECT "
          "CASE "
            "WHEN ( "
            "SELECT c.is_system_class "
            "FROM db_class c "
            "WHERE c.class_name = a.class_name AND c.owner_name = a.owner_name "
              ") = 'NO' THEN LOWER (a.owner_name) || '.' || a.class_name "
            "ELSE a.class_name "
            "END AS unique_name, "
          "b.key_attr_name, "
          "b.key_order + 1, "
          "a.index_name "
        "FROM "
          "db_index a, "
          "db_index_key b "
        "WHERE "
          "a.index_name = b.index_name "
          "AND a.class_name = b.class_name "
          "AMD a.owner_name = b.owner_name "
          "AND a.is_primary_key = 'YES' "
          "AND a.class_name = '";
    sql.append (class_name_only);
    sql.append ("' ");
    // *INDENT-ON*

    if (!schema_name.empty())
      {
        /* AND owner_name = UPPER ('%s') */
        sql.append ("AND owner_name = UPPER ('");
        sql.append (schema_name);
        sql.append ("') ");
      }

    sql.append ("ORDER BY b.key_attr_name");

    num_result = execute_schema_info_query (sql);
    if (num_result < 0)
      {
        return num_result;
      }
      }

    info.num_result = num_result;
    return NO_ERROR;
  }

  int schema_info_handler::sch_imported_keys (schema_info &info, std::string &fktable_name)
  {
    int error = NO_ERROR;
    int num_fk_info = 0, i = 0;
    int fk_i;
    DB_OBJECT *fktable_obj = db_find_class (fktable_name.c_str ());
    if (fktable_obj == NULL)
      {
    /* The followings are possible situations.  - A table matching fktable_name does not exist.  - User has no
     * authorization on the table.  - Other error we do not expect. In these cases, we will send an empty result. And
     * this rule is also applied to CCI_SCH_EXPORTED_KEYS and CCI_SCH_CROSS_REFERENCE. */
    info.num_result = num_fk_info;
    return NO_ERROR;
      }

    for (DB_CONSTRAINT *fk_const = db_get_constraints (fktable_obj); fk_const != NULL;
     fk_const = db_constraint_next (fk_const))
      {
    DB_CONSTRAINT_TYPE type = db_constraint_type (fk_const);
    if (type != DB_CONSTRAINT_FOREIGN_KEY)
      {
        continue;
      }
    SM_FOREIGN_KEY_INFO *fk_info = fk_const->fk_info;

    /* Find referenced table to get table name and columns. */
    DB_OBJECT *pktable_obj = db_get_foreign_key_ref_class (fk_const);
    if (pktable_obj == NULL)
      {
        m_error_ctx.set_error (db_error_code (), db_error_string (1), __FILE__, __LINE__);
        return ER_FAILED;
      }

    const char *pktable_name = db_get_class_name (pktable_obj);
    if (pktable_name == NULL)
      {
        m_error_ctx.set_error (db_error_code (), db_error_string (1), __FILE__, __LINE__);
        return ER_FAILED;
      }

    DB_CONSTRAINT *pktable_cons = db_get_constraints (pktable_obj);

    error = db_error_code ();
    if (error != NO_ERROR)
      {
        m_error_ctx.set_error (db_error_code (), db_error_string (1), __FILE__, __LINE__);
        return ER_FAILED;
      }

    DB_CONSTRAINT *pk = db_constraint_find_primary_key (pktable_cons);
    if (pk == NULL)
      {
        m_error_ctx.set_error (ER_FK_REF_CLASS_HAS_NOT_PK, "Referenced class has no primary key.", __FILE__, __LINE__);
        return ER_FAILED;
      }

    const char *pk_name = db_constraint_name (pk);
    DB_ATTRIBUTE **pk_attr = db_constraint_attributes (pk);
    if (pk_attr == NULL)
      {
        m_error_ctx.set_error (ER_SM_INVALID_CONSTRAINT, "Primary key has no attribute.", __FILE__, __LINE__);
        return ER_FAILED;
      }

    DB_ATTRIBUTE **fk_attr = db_constraint_attributes (fk_const);
    if (fk_attr == NULL)
      {
        m_error_ctx.set_error (ER_SM_INVALID_CONSTRAINT, "Foreign key has no attribute.", __FILE__, __LINE__);
        return ER_FAILED;
      }

    for (i = 0; pk_attr[i] != NULL && fk_attr[i] != NULL; i++)
      {
        // TODO: not implemented yet
        /*
          fk_res =
            add_fk_info_result (fk_res, pktable_name, db_attribute_name (pk_attr[i]), fktable_name,
                    db_attribute_name (fk_attr[i]), (short) i + 1, fk_info->update_action,
                    fk_info->delete_action, fk_info->name, pk_name, FK_INFO_SORT_BY_PKTABLE_NAME);
          if (fk_res == NULL)
        m_error_ctx.set_error (METHOD_CALLBACK_ER_NO_MORE_MEMORY, NULL, __FILE__, __LINE__);
            {
        return ER_FAILED;
            }
        */

        num_fk_info++;
      }

    /* pk_attr and fk_attr is null-terminated array. So, they should be null at this time. If one of them is not
     * null, it means that they have different number of attributes. */
    fk_i = (fk_attr[i] && IS_DEDUPLICATE_KEY_ATTR_ID (fk_attr[i]->id)) ? (i + 1) : i;
    assert (pk_attr[i] == NULL && fk_attr[fk_i] == NULL);
    if (pk_attr[i] != NULL || fk_attr[fk_i] != NULL)
      {
        m_error_ctx.set_error (ER_FK_NOT_MATCH_KEY_COUNT,
                   "The number of keys of the foreign key is different from that of the primary key.", __FILE__, __LINE__);
        return ER_FAILED;
      }
      }

    info.num_result = num_fk_info;
    return NO_ERROR;
  }

  int schema_info_handler::sch_exported_keys_or_cross_reference (schema_info &info,
      std::string &pktable_name,
      std::string &fktable_name, bool find_cross_ref)
  {
    // TODO
    return NO_ERROR;
  }

  int
  schema_info_handler::class_type (DB_OBJECT *class_obj)
  {
    int error = db_is_system_class (class_obj);

    if (error < 0)
      {
    m_error_ctx.set_error (error, NULL, __FILE__, __LINE__);
    return ER_FAILED;
      }
    if (error > 0)
      {
    return 0;
      }

    error = db_is_vclass (class_obj);
    if (error < 0)
      {
    m_error_ctx.set_error (error, NULL, __FILE__, __LINE__);
    return ER_FAILED;
      }
    if (error > 0)
      {
    return 1;
      }

    return 2;
  }

  int
  schema_info_handler::set_priv_table (std::vector<priv_table> &pts, int index, char *name, unsigned int class_priv)
  {
    int grant_opt = class_priv >> 8;
    int priv_type = 1;
    int num_tuple = 0;
    for (int i = 0; i < 7; i++)
      {
    if (class_priv & priv_type)
      {
        priv_table pt;
        pt.class_name.assign (name);
        pt.priv = priv_type;

        if (grant_opt & priv_type)
          {
        pt.grant = 1;
          }
        else
          {
        pt.grant = 0;
          }

        pts.push_back (pt);
        num_tuple++;
      }
    priv_type <<= 1;
      }

    return num_tuple;
  }

  void
  schema_info_handler::close_and_free_session ()
  {
    if (m_session)
      {
    db_close_session ((DB_SESSION *) (m_session));
      }
    m_session = NULL;
  }

  std::vector<column_info>
  get_schema_table_meta ()
  {
    column_info name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "NAME");
    column_info type (DB_TYPE_SHORT, DB_TYPE_NULL, 0, 0, lang_charset(), "TYPE");
    column_info remarks (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_CLASS_COMMENT_LENGTH, lang_charset(), "REMARKS");
    return std::vector<column_info> {name, type, remarks};
  }

  std::vector<column_info>
  get_schema_query_spec_meta ()
  {
    column_info query_spec (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "QUERY_SPEC");
    return std::vector<column_info> {query_spec};
  }

  std::vector<column_info>
  get_schema_attr_meta ()
  {
    column_info attr_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "ATTR_NAME");
    column_info domain (DB_TYPE_SHORT, DB_TYPE_NULL, 0, 0, lang_charset(), "DOMAIN");
    column_info scale (DB_TYPE_SHORT, DB_TYPE_NULL, 0, 0, lang_charset(), "SCALE");
    column_info precision (DB_TYPE_INTEGER, DB_TYPE_NULL, 0, 0, lang_charset(), "PRECISION");

    column_info indexed (DB_TYPE_SHORT, DB_TYPE_NULL, 0, 0, lang_charset(), "INDEXED");
    column_info non_null (DB_TYPE_SHORT, DB_TYPE_NULL, 0, 0, lang_charset(), "NON_NULL");
    column_info shared (DB_TYPE_SHORT, DB_TYPE_NULL, 0, 0, lang_charset(), "SHARED");
    column_info unique (DB_TYPE_SHORT, DB_TYPE_NULL, 0, 0, lang_charset(), "UNIQUE");

    column_info default_ (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "DEFAULT");
    column_info attr_order (DB_TYPE_INTEGER, DB_TYPE_NULL, 0, 0, lang_charset(), "ATTR_ORDER");

    column_info class_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "CLASS_NAME");
    column_info source_class (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "SOURCE_CLASS");
    column_info is_key (DB_TYPE_SHORT, DB_TYPE_NULL, 0, 0, lang_charset(), "IS_KEY");
    column_info remarks (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_CLASS_COMMENT_LENGTH, lang_charset(), "REMARKS");
    return std::vector<column_info> {attr_name, domain, scale, precision, indexed, non_null, shared, unique, default_, attr_order, class_name, source_class, is_key, remarks};
  }

  std::vector<column_info>
  get_schema_method_meta ()
  {
    column_info name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "NAME");
    column_info ret_domain (DB_TYPE_SHORT, DB_TYPE_NULL, 0, 0, lang_charset(), "RET_DOMAIN");
    column_info arg_domain (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "ARG_DOMAIN");
    return std::vector<column_info> {name, ret_domain, arg_domain};
  }

  std::vector<column_info>
  get_schema_methodfile_meta ()
  {
    column_info met_file (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "METHOD_FILE");
    return std::vector<column_info> {met_file};
  }

  std::vector<column_info>
  get_schema_superclasss_meta ()
  {
    column_info class_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "CLASS_NAME");
    column_info type (DB_TYPE_SHORT, DB_TYPE_NULL, 0, 0, lang_charset(), "TYPE");
    return std::vector<column_info> {class_name, type};
  }

  std::vector<column_info>
  get_schema_constraint_meta ()
  {
    column_info type (DB_TYPE_SHORT, DB_TYPE_NULL, 0, 0, lang_charset(), "TYPE");
    column_info name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "NAME");
    column_info attr_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "ATTR_NAME");
    column_info num_pages (DB_TYPE_INTEGER, DB_TYPE_NULL, 0, 0, lang_charset(), "NUM_PAGES");
    column_info num_keys (DB_TYPE_INTEGER, DB_TYPE_NULL, 0, 0, lang_charset(), "NUM_KEYS");
    column_info primary_key (DB_TYPE_SHORT, DB_TYPE_NULL, 0, 0, lang_charset(), "PRIMARY_KEY");
    column_info key_order (DB_TYPE_SHORT, DB_TYPE_NULL, 0, 0, lang_charset(), "KEY_ORDER");
    column_info asc_desc (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "ASC_DESC");
    return std::vector<column_info> {type, name, attr_name, num_pages, num_keys, primary_key, key_order, asc_desc};
  }

  std::vector<column_info>
  get_schema_trigger_meta ()
  {
    column_info name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "NAME");
    column_info status (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "STATUS");
    column_info event (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "EVENT");
    column_info target_class (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "TARGET_CLASS");
    column_info target_attr (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "TARGET_ATTR");
    column_info action_time (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "ACTION_TIME");
    column_info action (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "ACTION");
    column_info priority (DB_TYPE_FLOAT, DB_TYPE_NULL, 0, 0, lang_charset(), "PRIORITY");
    column_info condition_time (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(),
                "CONDITION_TIME");
    column_info condition (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "CONDITION");
    column_info remarks (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_CLASS_COMMENT_LENGTH, lang_charset(), "REMARKS");
    return std::vector<column_info> {name, status, event, target_class, target_attr, action_time, action, priority, condition_time, condition, remarks};

  }
  std::vector<column_info>
  get_schema_classpriv_meta ()
  {
    column_info class_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "CLASS_NAME");
    column_info privilege (DB_TYPE_STRING, DB_TYPE_NULL, 0, 10, lang_charset(), "PRIVILEGE");
    column_info grantable (DB_TYPE_STRING, DB_TYPE_NULL, 0, 5, lang_charset(), "GRANTABLE");
    return std::vector<column_info> {class_name, privilege, grantable};
  }

  std::vector<column_info>
  get_schema_attrpriv_meta ()
  {
    column_info attr_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "ATTR_NAME");
    column_info privilege (DB_TYPE_STRING, DB_TYPE_NULL, 0, 10, lang_charset(), "PRIVILEGE");
    column_info grantable (DB_TYPE_STRING, DB_TYPE_NULL, 0, 5, lang_charset(), "GRANTABLE");
    return std::vector<column_info> {attr_name, privilege, grantable};
  }

  std::vector<column_info>
  get_schema_directsuper_meta ()
  {
    column_info class_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "CLASS_NAME");
    column_info super_class_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(),
                  "SUPER_CLASS_NAME");
    return std::vector<column_info> {class_name, super_class_name};
  }

  std::vector<column_info>
  get_schema_primarykey_meta ()
  {
    column_info class_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "CLASS_NAME");
    column_info attr_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "ATTR_NAME");
    column_info num_keys (DB_TYPE_INTEGER, DB_TYPE_NULL, 0, 0, lang_charset(), "KEY_SEQ");
    column_info key_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "KEY_NAME");
    return std::vector<column_info> {class_name, attr_name, num_keys, key_name};
  }

  std::vector<column_info>
  get_schema_fk_info_meta ()
  {
    column_info pktable_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "PKTABLE_NAME");
    column_info pkcolumn_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "PKCOLUMN_NAME");
    column_info fktable_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "FKTABLE_NAME");
    column_info fkcolumn_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "FKCOLUMN_NAME");
    column_info key_seq (DB_TYPE_SHORT, DB_TYPE_NULL, 0, 0, lang_charset(), "KEY_SEQ");
    column_info update_rule (DB_TYPE_SHORT, DB_TYPE_NULL, 0, 0, lang_charset(), "UPDATE_RULE");
    column_info delete_rule (DB_TYPE_SHORT, DB_TYPE_NULL, 0, 0, lang_charset(), "DELETE_RULE");
    column_info fk_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "FK_NAME");
    column_info pk_name (DB_TYPE_STRING, DB_TYPE_NULL, 0, DB_MAX_IDENTIFIER_LENGTH, lang_charset(), "PK_NAME");
    return std::vector<column_info> {pktable_name, pkcolumn_name, fktable_name, fkcolumn_name, key_seq, update_rule, delete_rule, fk_name, pk_name};
  }
}