Skip to content

File class_description.cpp

File List > cubrid > src > object > class_description.cpp

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * 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.
 *
 */

/*
 * class_description.cpp
 */

#include "class_description.hpp"

#include "authenticate.h"
#include "class_object.h"
#include "locator_cl.h"
#include "mem_block.hpp"
#include "message_catalog.h"
#include "msgcat_help.hpp"
#include "object_printer.hpp"
#include "object_print_util.hpp"
#include "parse_tree.h"
#include "parser.h"
#include "schema_manager.h"
#include "string_buffer.hpp"

namespace
{
  void describe_trigger_list (tr_triglist *trig, string_buffer &sb, object_printer &printer, std::vector<char *> &v)
  {
    for (TR_TRIGLIST *t = trig; t != NULL; t = t->next)
      {
    sb.clear ();
    printer.describe_class_trigger (*t->trigger);
    v.push_back (object_print::copy_string (sb.get_buffer ()));
      }
  }

  void init_triggers (const sm_class &cls, struct db_object &dbo, string_buffer &sb, object_printer &printer,
              std::vector<char *> &triggers)
  {
    SM_ATTRIBUTE *attribute_p;
    TR_SCHEMA_CACHE *cache;
    int i;

    cache = cls.triggers;
    if (cache != NULL && !tr_validate_schema_cache (cache, &dbo))
      {
    for (i = 0; i < cache->array_length; i++)
      {
        describe_trigger_list (cache->triggers[i], sb, printer, triggers);
      }
      }

    for (attribute_p = cls.ordered_attributes; attribute_p != NULL; attribute_p = attribute_p->order_link)
      {
    cache = attribute_p->triggers;
    if (cache != NULL && !tr_validate_schema_cache (cache, &dbo))
      {
        for (i = 0; i < cache->array_length; i++)
          {
        describe_trigger_list (cache->triggers[i], sb, printer, triggers);
          }
      }
      }

    for (attribute_p = cls.class_attributes; attribute_p != NULL;
     attribute_p = (SM_ATTRIBUTE *) attribute_p->header.next)
      {
    cache = attribute_p->triggers;
    if (cache != NULL && !tr_validate_schema_cache (cache, &dbo))
      {
        for (i = 0; i < cache->array_length; i++)
          {
        describe_trigger_list (cache->triggers[i], sb, printer, triggers);
          }
      }
      }
  }
} //namespace

//former obj_print_make_class_help()
class_description::class_description ()
  : name (NULL)
  , class_type (NULL)
  , collation (NULL)
  , supers (NULL)
  , subs (NULL)
  , attributes (NULL)
  , class_attributes (NULL)
  , methods (NULL)
  , class_methods (NULL)
  , resolutions (NULL)
  , method_files (NULL)
  , query_spec (NULL)
  , object_id (NULL)
  , triggers ()
  , constraints (NULL)
  , partition ()
  , comment (NULL)
{
}

class_description::~class_description ()
{
  if (name != NULL)
    {
      free (name);
    }
  if (class_type != NULL)
    {
      free (class_type);
    }
  if (object_id != NULL)
    {
      free (object_id);
    }
  if (collation != NULL)
    {
      free (collation);
    }
  object_print::free_strarray (supers);
  object_print::free_strarray (subs);
  object_print::free_strarray (attributes);
  object_print::free_strarray (class_attributes);
  object_print::free_strarray (methods);
  object_print::free_strarray (class_methods);
  object_print::free_strarray (resolutions);
  object_print::free_strarray (method_files);
  object_print::free_strarray (query_spec);
#if 0 //bSolo: temporary until evolve above gcc 4.4.7
  for (auto it: triggers)
    {
      free (it);
    }
#else
  for (auto it=triggers.begin (); it != triggers.end (); ++it)
    {
      free (*it);
    }
#endif
  triggers.clear ();
  object_print::free_strarray (constraints);
#if 0 //bSolo: temporary until evolve above gcc 4.4.7
  for (auto it: partition)
    {
      free (it);
    }
#else
  for (auto it=partition.begin (); it != partition.end (); ++it)
    {
      free (*it);
    }
#endif
  partition.clear ();
  if (comment != NULL)
    {
      free (comment);
    }
}

int class_description::init (const char *name)
{
  db_object *op = sm_find_class (name);
  if (op == NULL)
    {
      int error_code = NO_ERROR;
      ASSERT_ERROR_AND_SET (error_code);
      return error_code;
    }
  return init (op, CSQL_SCHEMA_COMMAND);
}

int class_description::init (struct db_object *op, type prt_type)
{
  assert (op != NULL);
  string_buffer sb;
  return init (op, prt_type, sb);
}

int class_description::init (struct db_object *op, type prt_type, string_buffer &sb)
{
  assert (op != NULL);

  // cleanup before (re)initialize
  this->~class_description ();

  SM_CLASS *class_;
  SM_ATTRIBUTE *a;
  SM_METHOD *m;
  SM_QUERY_SPEC *p;
  DB_OBJLIST *super, *user;
  int count, i;
  char **strs;
  const char *kludge;
  int is_class = 0;
  SM_CLASS *subclass;
  bool include_inherited;
  bool force_print_att_coll = false;
  bool has_comment = false;
  int max_name_size = SM_MAX_IDENTIFIER_LENGTH + 50;
  size_t buf_size = 0;
  object_printer printer (sb);

  include_inherited = (prt_type == CSQL_SCHEMA_COMMAND);

  is_class = locator_is_class (op, DB_FETCH_READ);
  if (is_class < 0)
    {
      return ER_FAILED;
    }
  if (!is_class || locator_is_root (op))
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      return ER_FAILED;
    }

  int err = au_fetch_class (op, &class_, AU_FETCH_READ, AU_SELECT);
  if (err != NO_ERROR)
    {
      return err;
    }
  if (class_->comment != NULL && class_->comment[0] != '\0')
    {
      has_comment = true;
      max_name_size = SM_MAX_IDENTIFIER_LENGTH + SM_MAX_CLASS_COMMENT_LENGTH + 50;
    }

  force_print_att_coll = (class_->collation_id != LANG_SYS_COLLATION) ? true : false;
  /* make sure all the information is up to date */
  if (sm_clean_class (op, class_) != NO_ERROR)
    {
      return ER_FAILED;
    }

  if (prt_type == CSQL_SCHEMA_COMMAND)
    {
      /*
       * For the case of "print schema",
       * this->name is set to:
       *   exact class name
       *   + COLLATE collation_name if exists;
       *   + COMMENT 'text' if exists;
       *
       * The caller uses this->name to fill in "<Class Name> $name"
       */
      if (class_->collation_id == LANG_SYS_COLLATION)
    {
      sb.clear ();
      if (has_comment)
        {
          sb ("%-20s ", (char *) sm_ch_name ((MOBJ) class_));
          printer.describe_comment_for_session_cmd (class_->comment);
        }
      else
        {
          sb ("%s", (char *) sm_ch_name ((MOBJ) class_));
        }
    }
      else
    {
      if (has_comment)
        {
          sb ("%-20s COLLATE %s ", sm_ch_name ((MOBJ) class_), lang_get_collation_name (class_->collation_id));
          printer.describe_comment_for_session_cmd (class_->comment);
        }
      else
        {
          sb ("%-20s COLLATE %s", sm_ch_name ((MOBJ) class_), lang_get_collation_name (class_->collation_id));
        }
    }
      this->name = object_print::copy_string (sb.get_buffer ());
    }
  else
    {
      /*
       * For the case prt_type == OBJ_PRINT_SHOW_CREATE_TABLE
       * this->name is set to the exact class name
       */
      sb.clear ();
      sb ("[%s]", sm_remove_qualifier_name (sm_ch_name ((MOBJ) class_)));
      this->name = object_print::copy_string (sb.get_buffer ());
    }

  switch (class_->class_type)
    {
    default:
      this->class_type = object_print::copy_string (msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_HELP,
             MSGCAT_HELP_META_CLASS_HEADER));
      break;
    case SM_CLASS_CT:
      this->class_type = object_print::copy_string (msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_HELP,
             MSGCAT_HELP_CLASS_HEADER));
      break;
    case SM_VCLASS_CT:
      this->class_type = object_print::copy_string (msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_HELP,
             MSGCAT_HELP_VCLASS_HEADER));
      break;
    }

  this->collation = object_print::copy_string (lang_get_collation_name (class_->collation_id));
  if (this->collation == NULL)
    {
      return ER_FAILED;
    }

  if (has_comment && prt_type != CSQL_SCHEMA_COMMAND)
    {
      /*
       * For the case except "print schema",
       * comment is copied to this->comment anyway
       */
      this->comment = object_print::copy_string (class_->comment);
      if (this->comment == NULL)
    {
      return ER_FAILED;
    }
    }

  if (class_->inheritance != NULL)
    {
      count = ws_list_length ((DB_LIST *) class_->inheritance);
      buf_size = sizeof (char *) * (count + 1);

      strs = (char **) malloc (buf_size);
      if (strs == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
      return ER_FAILED;
    }

      i = 0;
      for (super = class_->inheritance; super != NULL; super = super->next)
    {
      /* kludge for const vs. non-const warnings */
      kludge = sm_get_ch_name (super->op);
      if (kludge == NULL)
        {
          assert (er_errid () != NO_ERROR);
          return ER_FAILED;
        }

      if (prt_type == CSQL_SCHEMA_COMMAND)
        {
          strs[i] = object_print::copy_string ((char *) kludge);
        }
      else
        {
          /* prt_type == OBJ_PRINT_SHOW_CREATE_TABLE */
          sb.clear ();
          sb ("[%s]", kludge);
          strs[i] = object_print::copy_string (sb.get_buffer ());
        }
      i++;
    }

      strs[i] = 0;
      this->supers = strs;
    }

  if (class_->users != NULL)
    {
      count = ws_list_length ((DB_LIST *) class_->users);
      buf_size = sizeof (char *) * (count + 1);

      strs = (char **) malloc (buf_size);
      if (strs == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
      return ER_FAILED;
    }

      i = 0;
      for (user = class_->users; user != NULL; user = user->next)
    {
      /* kludge for const vs. non-const warnings */
      kludge = sm_get_ch_name (user->op);
      if (kludge == NULL)
        {
          assert (er_errid () != NO_ERROR);
          return ER_FAILED;
        }

      if (prt_type == CSQL_SCHEMA_COMMAND)
        {
          strs[i] = object_print::copy_string ((char *) kludge);
        }
      else
        {
          /* prt_type == OBJ_PRINT_SHOW_CREATE_TABLE */
          sb.clear ();
          sb ("[%s]", kludge);
          strs[i] = object_print::copy_string (sb.get_buffer ());
        }

      i++;
    }

      strs[i] = 0;
      this->subs = strs;
    }

  if (class_->attributes != NULL || class_->shared != NULL)
    {
      if (include_inherited)
    {
      count = class_->att_count + class_->shared_count;
    }
      else
    {
      count = 0;
      /* find the number own by itself */
      for (a = class_->ordered_attributes; a != NULL; a = a->order_link)
        {
          if (a->class_mop == op)
        {
          count++;
        }
        }
    }

      if (count > 0)
    {
      buf_size = sizeof (char *) * (count + 1);

      strs = (char **) malloc (buf_size);
      if (strs == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
          return ER_FAILED;
        }

      i = 0;
      for (a = class_->ordered_attributes; a != NULL; a = a->order_link)
        {
          if (include_inherited || a->class_mop == op)
        {
          sb.clear ();
          printer.describe_attribute (*op, *a, (a->class_mop != op), prt_type, force_print_att_coll);
          if (sb.len () == 0)
            {
              return ER_FAILED;
            }
          strs[i] = object_print::copy_string (sb.get_buffer ());
          i++;
        }
        }

      strs[i] = 0;
      this->attributes = strs;
    }
    }

  if (class_->class_attributes != NULL)
    {
      if (include_inherited)
    {
      count = class_->class_attribute_count;
    }
      else
    {
      count = 0;
      /* find the number own by itself */
      for (a = class_->class_attributes; a != NULL; a = (SM_ATTRIBUTE *) a->header.next)
        {
          if (a->class_mop == op)
        {
          count++;
        }
        }
    }

      if (count > 0)
    {
      buf_size = sizeof (char *) * (count + 1);

      strs = (char **)malloc (buf_size);
      if (strs == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
          return ER_FAILED;
        }

      i = 0;
      for (a = class_->class_attributes; a != NULL; a = (SM_ATTRIBUTE *) a->header.next)
        {
          if (include_inherited || a->class_mop == op)
        {
          sb.clear ();
          printer.describe_attribute (*op, *a, (a->class_mop != op), prt_type, force_print_att_coll);
          if (sb.len () == 0)
            {
              return ER_FAILED;
            }
          strs[i] = object_print::copy_string (sb.get_buffer ());
          i++;
        }
        }

      strs[i] = 0;
      this->class_attributes = strs;
    }
    }

  if (class_->methods != NULL)
    {
      if (include_inherited)
    {
      count = class_->method_count;
    }
      else
    {
      count = 0;
      /* find the number own by itself */
      for (m = class_->methods; m != NULL; m = (SM_METHOD *) m->header.next)
        {
          if (m->class_mop == op)
        {
          count++;
        }
        }
    }

      if (count > 0)
    {
      buf_size = sizeof (char *) * (count + 1);

      strs = (char **) malloc (buf_size);
      if (strs == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
          return ER_FAILED;
        }

      i = 0;
      for (m = class_->methods; m != NULL; m = (SM_METHOD *) m->header.next)
        {
          if (include_inherited || m->class_mop == op)
        {
          sb.clear ();
          printer.describe_method (*op, *m, prt_type);
          strs[i] = object_print::copy_string (sb.get_buffer ());
          i++;
        }
        }

      strs[i] = 0;
      this->methods = strs;
    }
    }

  if (class_->class_methods != NULL)
    {
      if (include_inherited)
    {
      count = class_->class_method_count;
    }
      else
    {
      count = 0;
      /* find the number own by itself */
      for (m = class_->class_methods; m != NULL; m = (SM_METHOD *) m->header.next)
        {
          if (m->class_mop == op)
        {
          count++;
        }
        }
    }

      if (count > 0)
    {
      buf_size = sizeof (char *) * (count + 1);

      strs = (char **) malloc (buf_size);
      if (strs == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
          return ER_FAILED;
        }

      i = 0;
      for (m = class_->class_methods; m != NULL; m = (SM_METHOD *) m->header.next)
        {
          if (include_inherited || m->class_mop == op)
        {
          sb.clear ();
          printer.describe_method (*op, *m, prt_type);
          strs[i] = object_print::copy_string (sb.get_buffer ());
          i++;
        }
        }

      strs[i] = 0;
      this->class_methods = strs;
    }
    }

  if (class_->resolutions != NULL)
    {
      count = ws_list_length ((DB_LIST *) class_->resolutions);
      buf_size = sizeof (char *) * (count + 1);

      strs = (char **) malloc (buf_size);
      if (strs == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
      return ER_FAILED;
    }

      i = 0;
      for (SM_RESOLUTION *r = class_->resolutions; r != NULL; r = r->next)
    {
      sb.clear ();
      printer.describe_resolution (*r, prt_type);
      strs[i] = object_print::copy_string (sb.get_buffer ());
      i++;
    }

      strs[i] = 0;
      this->resolutions = strs;
    }

  if (class_->method_files != NULL)
    {
      if (include_inherited)
    {
      count = ws_list_length ((DB_LIST *) class_->method_files);
    }
      else
    {
      count = 0;

      /* find the number own by itself */
      for (SM_METHOD_FILE *f = class_->method_files; f != NULL; f = f->next)
        {
          if (f->class_mop == op)
        {
          count++;
        }
        }
    }

      if (count > 0)
    {
      buf_size = sizeof (char *) * (count + 1);

      strs = (char **) malloc (buf_size);
      if (strs == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
          return ER_FAILED;
        }

      i = 0;
      for (SM_METHOD_FILE *f = class_->method_files; f != NULL; f = f->next)
        {
          if (include_inherited || f->class_mop == op)
        {
          sb.clear ();
          printer.describe_method_file (*op, *f);
          strs[i] = object_print::copy_string (sb.get_buffer ());
          i++;
        }
        }

      strs[i] = 0;
      this->method_files = strs;
    }
    }

  if (class_->query_spec != NULL)
    {
      count = ws_list_length ((DB_LIST *) class_->query_spec);
      buf_size = sizeof (char *) * (count + 1);

      strs = (char **) malloc (buf_size);
      if (strs == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
      return ER_FAILED;
    }

      i = 0;
      for (p = class_->query_spec; p != NULL; p = p->next)
    {
      strs[i] = object_print::copy_string ((char *) p->specification);
      i++;
    }

      strs[i] = 0;
      this->query_spec = strs;
    }

  /* these are a bit more complicated */
  init_triggers (*class_, *op, sb, printer, triggers);

  /*
   * Process multi-column class constraints (Unique and Indexes).
   * Single column constraints (NOT 0) are displayed along with
   * the attributes.
   */
  this->constraints = 0;    /* initialize */
  if (class_->constraints != NULL)
    {
      SM_CLASS_CONSTRAINT *c;

      count = 0;
      for (c = class_->constraints; c; c = c->next)
    {
      if (SM_IS_CONSTRAINT_INDEX_FAMILY (c->type))
        {
          /* Csql schema command will print all constraints, which include the constraints belong to the table
           * itself and belong to the parent table. But show create table will only print the constraints which
           * belong to the table itself.
           */
          assert ( (c->attributes[0] && IS_DEDUPLICATE_KEY_ATTR_ID (c->attributes[0]->id)) ? (c->attributes[1] != NULL) : true);
          if (include_inherited
          || (c->attributes[0] && c->attributes[ ((IS_DEDUPLICATE_KEY_ATTR_ID (c->attributes[0]->id)) ? 1 : 0)]->class_mop == op))
        {
          count++;
        }
        }
    }

      if (count > 0)
    {
      buf_size = sizeof (char *) * (count + 1);

      strs = (char **) malloc (buf_size);
      if (strs == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
          return ER_FAILED;
        }

      i = 0;
      for (c = class_->constraints; c; c = c->next)
        {
          if (SM_IS_CONSTRAINT_INDEX_FAMILY (c->type))
        {
          assert ( (c->attributes[0] && IS_DEDUPLICATE_KEY_ATTR_ID (c->attributes[0]->id)) ? (c->attributes[1] != NULL) : true);
          if (include_inherited
              || (c->attributes[0] && c->attributes[ ((IS_DEDUPLICATE_KEY_ATTR_ID (c->attributes[0]->id)) ? 1 : 0)]->class_mop == op))
            {
              sb.clear ();
              printer.describe_constraint (*class_, *c, prt_type);
              strs[i] = object_print::copy_string (sb.get_buffer ());
              if (strs[i] == NULL)
            {
              this->constraints = strs;
              return ER_FAILED;
            }
              i++;
            }
        }
        }

      strs[i] = 0;
      this->constraints = strs;
    }
    }

  //partition
  if (class_->partition != NULL && class_->partition->pname == NULL)
    {
      sb.clear ();
      printer.describe_partition_info (*class_->partition);
      partition.push_back (object_print::copy_string (sb.get_buffer ()));

      bool is_print_partition = true;
      count = 0;

      /* Show create table will not print the sub partition for hash partition table. */
      if (prt_type == SHOW_CREATE_TABLE)
    {
      is_print_partition = (class_->partition->partition_type != PT_PARTITION_HASH);
    }

      if (is_print_partition)
    {
      for (user = class_->users; user != NULL; user = user->next)
        {
          if (au_fetch_class (user->op, &subclass, AU_FETCH_READ, AU_SELECT) != NO_ERROR)
        {
          return ER_FAILED;
        }
          if (subclass->partition)
        {
          sb.clear ();
          printer.describe_partition_parts (*subclass->partition, prt_type);
          partition.push_back (object_print::copy_string (sb.get_buffer ()));
        }
        }
    }
    }

  return NO_ERROR;
}