Skip to content

File elo.c

File List > cubrid > src > object > elo.c

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.
 *
 */

/*
 * elo.c - ELO interface
 *
 * MEMOERY MANAGEMENT
 *   db_private_alloc/db_private_free

 * ERROR HANDLING
 *   Every exported function sets error code if error occurred
 *   Assume argument checking is already done.
 *
 * LOCATOR FORMAT: <scheme name>:<scheme specific data>
 *   <scheme name>
 *     inline : data is encoded in locator
 *     <other>: for server side BLOB/CLOB storage extension.
 *
 *   <scheme specific data>
 *     inline : 2byte hexa decimal code encoding
 *     <other>: scheme_name specific part (EXTERNAL STORAGE)
 *
 * META DATA
 * - simple key, value pair
 * - format: <key>=<value>[,<key>=<value>[...]]
 *           key, value should not contain '=', or ',' character
 */

#ident "$Id$"

#include "config.h"

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stddef.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>

#include "elo.h"

#include "boot.h"
#include "dbtype.h"
#include "error_manager.h"
#include "es.h"
#include "lob_locator.hpp"
#include "object_primitive.h"
#include "object_representation.h"
#include "storage_common.h"
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

static const DB_ELO elo_Initializer = { -1LL, NULL, NULL, ELO_NULL, ES_NONE };

#define ELO_NEEDS_TRANSACTION(e) \
        ((e)->es_type == ES_OWFS || (e)->es_type == ES_POSIX)

/* ========================================================================== */
/* EXPORTED FUNCTIONS */
/* ========================================================================== */

/*
 * elo_create () - create a new file
 * return: NO_ERROR if successful, error code otherwise
 * DB_ELO(in): DB_ELO
 * type(in): DB_ELO_TYPE
 */
int
elo_create (DB_ELO * elo)
{
  ES_URI out_uri;
  char *uri = NULL;
  int ret = NO_ERROR;

  assert (elo != NULL);


  ret = es_create_file (out_uri);
  if (ret < NO_ERROR)
    {
      ASSERT_ERROR ();
      return ret;
    }

  uri = db_private_strdup (NULL, out_uri);
  if (uri == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, strlen (out_uri));
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }

  elo_init_structure (elo);
  elo->size = 0;
  elo->locator = uri;
  elo->type = ELO_FBO;
  elo->es_type = es_get_type (uri);

  if (ELO_NEEDS_TRANSACTION (elo))
    {
      ret = lob_locator_add (elo->locator, LOB_TRANSIENT_CREATED);
      if (ret != NO_ERROR)
    {
      ASSERT_ERROR ();
    }
    }
  return ret;
}

/*
 * elo_init_structure () - init. ELO structure
 */
void
elo_init_structure (DB_ELO * elo)
{
  if (elo != NULL)
    {
      *elo = elo_Initializer;
    }
}

/*
 * elo_copy_structure () - Copy elo instance
 * return:
 * elo(in):
 * dest(out):
 */
int
elo_copy_structure (const DB_ELO * elo, DB_ELO * dest)
{
  char *locator = NULL;
  char *meta_data = NULL;

  assert (elo != NULL);
  assert (dest != NULL);

  if (elo->locator != NULL)
    {
      locator = db_private_strdup (NULL, elo->locator);
      if (locator == NULL)
    {
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }
    }

  if (elo->meta_data != NULL)
    {
      meta_data = db_private_strdup (NULL, elo->meta_data);
      if (meta_data == NULL)
    {
      if (locator != NULL)
        {
          db_private_free_and_init (NULL, locator);
        }
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }
    }

  *dest = *elo;
  dest->locator = locator;
  dest->meta_data = meta_data;

  return NO_ERROR;
}

/*
 * elo_free_structure () - free DB_ELO structure
 *
 * return:
 * elo(in):
 */
void
elo_free_structure (DB_ELO * elo)
{
  if (elo != NULL)
    {
      if (elo->locator != NULL)
    {
      db_private_free_and_init (NULL, elo->locator);
    }

      if (elo->meta_data != NULL)
    {
      db_private_free_and_init (NULL, elo->meta_data);
    }

      elo_init_structure (elo);
    }
}

/*
 * elo_copy () - Create new elo instance and copy the file located by elo.
 * return:
 * elo(in):
 * dest(out):
 */
int
elo_copy (DB_ELO * elo, DB_ELO * dest)
{
  int ret;
  ES_URI out_uri;
  char *locator = NULL;
  char *meta_data = NULL;

  assert (elo != NULL);
  assert (dest != NULL);
  assert (elo->type == ELO_FBO);
  assert (elo->locator != NULL);

  /* create elo instance and copy file */
  if (elo->meta_data != NULL)
    {
      meta_data = db_private_strdup (NULL, elo->meta_data);
      if (meta_data == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }
    }

  /* if it uses external storage, do transaction work */
  elo->es_type = es_get_type (elo->locator);
  if (!ELO_NEEDS_TRANSACTION (elo))
    {
      ret = es_copy_file (elo->locator, elo->meta_data, out_uri);
      if (ret != NO_ERROR)
    {
      goto error_return;
    }

      locator = db_private_strdup (NULL, out_uri);
      if (locator == NULL)
    {
      es_delete_file (out_uri);
      goto error_return;
    }
    }
  else
    {
      LOB_LOCATOR_STATE state;
      ES_URI real_locator;

      state = lob_locator_find (elo->locator, real_locator);
      switch (state)
    {
    case LOB_TRANSIENT_CREATED:
    case LOB_PERMANENT_DELETED:
      {
        ret = es_rename_file (real_locator, elo->meta_data, out_uri);
        if (ret != NO_ERROR)
          {
        goto error_return;
          }
        locator = db_private_strdup (NULL, out_uri);
        if (locator == NULL)
          {
        assert (er_errid () != NO_ERROR);
        ret = er_errid ();
        goto error_return;
          }
        ret = lob_locator_change_state (elo->locator, locator, LOB_PERMANENT_CREATED);
        if (ret != NO_ERROR)
          {
        goto error_return;
          }
      }
      break;

    case LOB_TRANSIENT_DELETED:
      {
        locator = db_private_strdup (NULL, elo->locator);
        if (locator == NULL)
          {
        assert (er_errid () != NO_ERROR);
        ret = er_errid ();
        goto error_return;
          }
        ret = lob_locator_drop (elo->locator);
        if (ret != NO_ERROR)
          {
        goto error_return;
          }
      }
      break;

    case LOB_PERMANENT_CREATED:
    case LOB_NOT_FOUND:
      {
        ret = es_copy_file (real_locator, elo->meta_data, out_uri);
        if (ret != NO_ERROR)
          {
        goto error_return;
          }
        locator = db_private_strdup (NULL, out_uri);
        if (locator == NULL)
          {
        es_delete_file (out_uri);
        goto error_return;
          }
        ret = lob_locator_add (locator, LOB_PERMANENT_CREATED);
        if (ret != NO_ERROR)
          {
        goto error_return;
          }
      }
      break;

    case LOB_UNKNOWN:
      assert (er_errid () != NO_ERROR);
      ret = er_errid ();
      goto error_return;

    default:
      assert (0);
      return ER_FAILED;
    }
    }

  *dest = *elo;
  dest->locator = locator;
  dest->meta_data = meta_data;

  return NO_ERROR;

error_return:
  if (locator != NULL)
    {
      db_private_free_and_init (NULL, locator);
    }

  if (meta_data != NULL)
    {
      db_private_free_and_init (NULL, meta_data);
    }

  return ret;
}

/*
 * elo_copy_with_prefix () - Relocate an ELO to a new path with a given prefix.
 * return: error code
 * src_elo(in): DB_ELO structure representing the original source file.
 * prefix(in): Path prefix to be added to the destination location.
 * dest_elo(out): DB_ELO structure representing the relocated file.
 *
 * Note: This function operates similarly to elo_copy (), but with the following differences:
 * 1. It adds a prefix to the destination path.
 * 2. In LOB_TRANSIENT_CREATED or LOB_PERMANENT_DELETED states, it performs a rename followed by a move operation.
 * 3. Currently, CUBRID supports only the ES_POSIX type, so only the ES_POSIX case is handled.
 */
int
elo_copy_with_prefix (DB_ELO * src_elo, const char *prefix, DB_ELO * dest_elo)
{
  int ret = NO_ERROR;
  ES_URI out_uri;
  char *locator = NULL;
  char *meta_data = NULL;

  assert (src_elo != NULL);
  assert (prefix != NULL);
  assert (dest_elo != NULL);
  assert (src_elo->type == ELO_FBO);
  assert (src_elo->locator != NULL);

  if (src_elo->meta_data != NULL)
    {
      meta_data = db_private_strdup (NULL, src_elo->meta_data);
      if (meta_data == NULL)
    {
      assert (er_errid () != NO_ERROR);
      ret = er_errid ();

      goto error_return;
    }
    }

  src_elo->es_type = es_get_type (src_elo->locator);
  /* CUBRID supports only the ES_POSIX type, so the ELO_NEEDS_TRANSACTION() check is not performed. */
  if (src_elo->es_type == ES_POSIX)
    {
      LOB_LOCATOR_STATE state;
      ES_URI real_locator;

      state = lob_locator_find (src_elo->locator, real_locator);
      switch (state)
    {
    case LOB_TRANSIENT_CREATED:
    case LOB_PERMANENT_DELETED:
      {
        ret = es_move_file_with_prefix (real_locator, src_elo->meta_data, prefix, out_uri);
        if (ret != NO_ERROR)
          {
        goto error_return;
          }

        locator = db_private_strdup (NULL, out_uri);
        if (locator == NULL)
          {
        assert (er_errid () != NO_ERROR);
        ret = er_errid ();
        es_delete_file (out_uri);
        goto error_return;
          }

        ret = lob_locator_change_state (src_elo->locator, locator, LOB_PERMANENT_CREATED);
        if (ret != NO_ERROR)
          {
        es_delete_file (out_uri);
        goto error_return;
          }
      }
      break;

    case LOB_TRANSIENT_DELETED:
      {
        locator = db_private_strdup (NULL, src_elo->locator);
        if (locator == NULL)
          {
        assert (er_errid () != NO_ERROR);
        ret = er_errid ();
        goto error_return;
          }
        ret = lob_locator_drop (src_elo->locator);
        if (ret != NO_ERROR)
          {
        goto error_return;
          }
      }
      break;

    case LOB_PERMANENT_CREATED:
    case LOB_NOT_FOUND:
      {
        ret = es_copy_file_with_prefix (real_locator, src_elo->meta_data, prefix, out_uri);
        if (ret != NO_ERROR)
          {
        goto error_return;
          }
        locator = db_private_strdup (NULL, out_uri);
        if (locator == NULL)
          {
        assert (er_errid () != NO_ERROR);
        ret = er_errid ();
        es_delete_file (out_uri);
        goto error_return;
          }
        ret = lob_locator_add (locator, LOB_PERMANENT_CREATED);
        if (ret != NO_ERROR)
          {
        es_delete_file (out_uri);
        goto error_return;
          }
      }
      break;

    case LOB_UNKNOWN:
      assert (er_errid () != NO_ERROR);
      ret = er_errid ();
      goto error_return;

    default:
      assert (false);
      ret = ER_FAILED;
      goto error_return;
    }
    }
  else
    {
      assert (false);
      ret = ER_ES_GENERAL;
      goto error_return;
    }

  *dest_elo = *src_elo;
  dest_elo->locator = locator;
  dest_elo->meta_data = meta_data;

  return NO_ERROR;

error_return:
  db_private_free_and_init (NULL, locator);
  db_private_free_and_init (NULL, meta_data);

  return ret;
}

/*
 * elo_delete () - delete the file located by elo and the structure itself.
 * return: NO_ERROR if successful, error code otherwise
 * elo(in):
 */
int
elo_delete (DB_ELO * elo, bool force_delete)
{
  int ret = NO_ERROR;

  assert (elo != NULL);
  assert (elo->type == ELO_FBO);
  assert (elo->locator != NULL);

  /* if it uses external storage, do transaction work */
  elo->es_type = es_get_type (elo->locator);
  if (!ELO_NEEDS_TRANSACTION (elo) || force_delete)
    {
      ret = es_delete_file (elo->locator);
    }
  else
    {
      LOB_LOCATOR_STATE state;
      ES_URI real_locator;

      state = lob_locator_find (elo->locator, real_locator);
      switch (state)
    {
    case LOB_TRANSIENT_CREATED:
      ret = es_delete_file (real_locator);
      if (ret != NO_ERROR)
        {
          return ret;
        }

      ret = lob_locator_drop (elo->locator);
      break;

    case LOB_PERMANENT_CREATED:
    case LOB_PERMANENT_DELETED:
      ret = lob_locator_change_state (elo->locator, NULL, LOB_PERMANENT_DELETED);
      break;

    case LOB_TRANSIENT_DELETED:
      break;

    case LOB_NOT_FOUND:
      ret = lob_locator_add (elo->locator, LOB_TRANSIENT_DELETED);
      break;

    case LOB_UNKNOWN:
      assert (er_errid () != NO_ERROR);
      ret = er_errid ();
      break;

    default:
      assert (0);
      return ER_FAILED;
    }
    }

  return ret;
}

/*
 * elo_size () - get the size of file located by elo
 * return: non-negative integer if successful, -1 if error.
 * elo(in):
 */
off_t
elo_size (DB_ELO * elo)
{
  off_t ret;

  assert (elo != NULL);
  assert (elo->type == ELO_FBO);
  assert (elo->locator != NULL);

  if (elo->size >= 0)
    {
      return elo->size;
    }

  ret = es_get_file_size (elo->locator);
  if (ret < 0)
    {
      return ret;
    }

  elo->size = ret;
  return ret;
}

/*
 * elo_read () - read data from elo
 * return: bytes read if successful, -1 if error
 * elo(in): ELO instance
 * buf(out): output buffer
 * count(in): bytes to read
 *
 */
ssize_t
elo_read (const DB_ELO * elo, off_t pos, void *buf, size_t count)
{
  ssize_t ret;

  assert (elo != NULL);
  assert (pos >= 0);
  assert (buf != NULL);
  assert (elo->type == ELO_FBO);
  assert (elo->locator != NULL);

  ret = es_read_file (elo->locator, buf, count, pos);
  if (ret < NO_ERROR)
    {
      ASSERT_ERROR ();
    }
  return ret;
}

/*
 * elo_write () - write data to elo
 * return: bytes written if successful, -1 if error
 * elo(in): ELO instance
 * buf(out): data buffer
 * count(in): bytes to write
 */
ssize_t
elo_write (DB_ELO * elo, off_t pos, const void *buf, size_t count)
{
  ssize_t ret;

  assert (elo != NULL);
  assert (pos >= 0);
  assert (buf != NULL);
  assert (count > 0);
  assert (elo->type == ELO_FBO);
  assert (elo->locator != NULL);

  ret = es_write_file (elo->locator, buf, count, pos);
  if (ret < 0)
    {
      ASSERT_ERROR ();
      return ret;
    }

  /* adjust size field */
  if ((INT64) (pos + count) > elo->size)
    {
      elo->size = pos + count;
    }
  return ret;
}