/*
 * Copyright (C) 2008 Search Solution Corporation. All rights reserved by Search Solution.
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; version 2 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

/*
 * db_set.c - API functions for manipulating sets & sequences.
 */

#ident "$Id$"

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <string.h>

#include "system_parameter.h"
#include "storage_common.h"
#include "db.h"
#include "class_object.h"
#include "server_interface.h"
#include "set_object.h"

#if !defined(SERVER_MODE)
#include "object_print.h"
#include "boot_cl.h"
#include "locator_cl.h"
#include "schema_manager.h"
#include "schema_template.h"
#include "object_accessor.h"
#include "virtual_object.h"
#endif

#include "parser.h"

#include "dbval.h"		/* this must be the last header file included!!! */

#define ERROR_SET(error, code) \
  do {                     \
    error =  code;         \
    er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, code, 0); \
  } while (0)

#define ERROR_SET1(error, code, arg1) \
  do {                            \
    error = code;                 \
    er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, code, 1, arg1); \
  } while (0)

/*
 *  SET CREATION FUNCTIONS
 */

/*
 * db_set_create() - This function creates an empty set for an attribute.
 *    The type of set(set, multiset, or sequence) is determined by examining
 *    the attribute that is specified.
 * return : set descriptor
 * classop(in): class or instance pointer
 * name(in): attribute name
 *
 * note : The returned set is not immediately given to the attribute
 *   it must be given to an attribute later using a db_put() call
 */
DB_SET *
db_set_create (MOP classop, const char *name)
{
  DB_SET *set;
#if !defined(SERVER_MODE)
  int error = NO_ERROR;
#endif

  CHECK_CONNECT_NULL ();

  set = NULL;
  if (classop != NULL && name != NULL)
    {
#if !defined(SERVER_MODE)
      SM_CLASS *class_;
      SM_ATTRIBUTE *att;

      if (au_fetch_class (classop, &class_, AU_FETCH_READ, AU_SELECT) ==
	  NO_ERROR)
	{
	  att = classobj_find_attribute (class_, name, 0);
	  if (att == NULL)
	    {
	      ERROR_SET1 (error, ER_OBJ_INVALID_ATTRIBUTE, name);
	    }
	  else
	    {
	      if (att->type->id == DB_TYPE_SET)
		{
		  set = set_create_basic ();
		}
	      else if (att->type->id == DB_TYPE_MULTI_SET)
		{
		  set = set_create_multi ();
		}
	      else if (att->type->id == DB_TYPE_SEQUENCE)
		{
		  set = set_create_sequence (0);
		}
	      else
		{
		  ERROR_SET1 (error, ER_OBJ_DOMAIN_CONFLICT, name);
		}
	    }
	}
#endif
    }

  return (set);
}

/*
 * db_set_create_basic() - This function creates a basic set for an attribute.
 *    The class and name arguments can be set to NULL. If values are supplied,
 *    a check will be made to make sure that the attribute was defined with the
 *    set domain.
 * return : set descriptor
 * classop(in): class or instance
 * name(in): attribute name
 *
 * note : The new set will not be attached to any object, so you must use the
 *   db_put() function to assign it as the value of an attribute.
 */
DB_SET *
db_set_create_basic (MOP classop, const char *name)
{
  DB_SET *set;
#if !defined(SERVER_MODE)
  int error = NO_ERROR;
#endif

  CHECK_CONNECT_NULL ();

  set = NULL;
  if (classop == NULL || name == NULL)
    {
      set = set_create_basic ();
    }
  else
    {
#if !defined(SERVER_MODE)
      SM_CLASS *class_;
      SM_ATTRIBUTE *att;

      if (au_fetch_class (classop, &class_, AU_FETCH_READ, AU_SELECT) ==
	  NO_ERROR)
	{
	  att = classobj_find_attribute (class_, name, 0);
	  if (att == NULL)
	    {
	      ERROR_SET1 (error, ER_OBJ_INVALID_ATTRIBUTE, name);
	    }
	  else
	    {
	      if (att->type->id == DB_TYPE_SET)
		{
		  set = set_create_basic ();
		}
	      else
		{
		  ERROR_SET1 (error, ER_OBJ_DOMAIN_CONFLICT, name);
		}
	    }
	}
#endif
    }

  return (set);
}

/*
 * db_set_create_multi() - This function creates an empty multiset. The class
 *    and name arguments can be set to NULL. If values are supplied, a check
 *    will be made to make sure that the attribute was defined with the
 *    multiset domain.
 * return : set descriptor
 * classop(in): class or instance
 * name(in): attribute name
 *
 * note : The new set will not be attached to any object, so you must use the
 *    db_put() function to assign it as the value of an attribute.
 */
DB_SET *
db_set_create_multi (MOP classop, const char *name)
{
  DB_SET *set;
#if !defined(SERVER_MODE)
  int error = NO_ERROR;
#endif

  CHECK_CONNECT_NULL ();

  set = NULL;
  if (classop == NULL || name == NULL)
    {
      set = set_create_multi ();
    }
  else
    {
#if !defined(SERVER_MODE)
      SM_CLASS *class_;
      SM_ATTRIBUTE *att;

      if (au_fetch_class (classop, &class_, AU_FETCH_READ, AU_SELECT) ==
	  NO_ERROR)
	{
	  att = classobj_find_attribute (class_, name, 0);
	  if (att == NULL)
	    {
	      ERROR_SET1 (error, ER_OBJ_INVALID_ATTRIBUTE, name);
	    }
	  else
	    {
	      if (att->type->id == DB_TYPE_MULTI_SET)
		{
		  set = set_create_multi ();
		}
	      else
		{
		  ERROR_SET1 (error, ER_OBJ_DOMAIN_CONFLICT, name);
		}
	    }
	}
#endif
    }

  return (set);
}

/*
 * db_seq_create() - This function creates an empty sequence. The class and
 *    name arguments can be set to NULL. If values are supplied, a check will
 *    be made to make sure that the attribute was defined with the sequence
 *    domain.
 * return : a set (sequence) descriptor
 * classop(in): class or instance
 * name(in): attribute name
 * size(in): initial size
 *
 * note : The new set will not be attached to any object, so you must use the
 *    db_put( ) function to assign it as the value of an attribute. If the size
 *    is not known, it is permissible to pass zero.
 */
DB_SET *
db_seq_create (MOP classop, const char *name, int size)
{
  DB_SET *set;
#if !defined(SERVER_MODE)
  int error = NO_ERROR;
#endif

  CHECK_CONNECT_NULL ();

  set = NULL;
  if (classop == NULL || name == NULL)
    {
      set = set_create_sequence (size);
    }
  else
    {
#if !defined(SERVER_MODE)
      SM_CLASS *class_;
      SM_ATTRIBUTE *att;

      if (au_fetch_class (classop, &class_, AU_FETCH_READ, AU_SELECT) ==
	  NO_ERROR)
	{
	  att = classobj_find_attribute (class_, name, 0);
	  if (att == NULL)
	    {
	      ERROR_SET1 (error, ER_OBJ_INVALID_ATTRIBUTE, name);
	    }
	  else
	    {
	      if (att->type->id == DB_TYPE_SEQUENCE)
		{
		  set = set_create_sequence (size);
		}
	      else
		{
		  ERROR_SET1 (error, ER_OBJ_DOMAIN_CONFLICT, name);
		}
	    }
	}
#endif
    }

  return (set);
}

/*
 * db_set_free() - This function frees a set handle. If the set is owned by an
 *    object, the contents of the set are not freed, only the set handle is
 *    freed. If the set is not owned by an object, the handle and all of the
 *    elements are freed.
 * return : error code
 * set(in): set descriptor
 */
int
db_set_free (DB_SET * set)
{
  /* don't check connection here, we always allow things to be freed */
  if (set != NULL)
    {
      set_free (set);
    }

  return (NO_ERROR);
}

/*
 * db_seq_free() -
 * return : error code
 * seq(in): set descriptor
 */
int
db_seq_free (DB_SEQ * seq)
{
  /* don't check connection here, we always allow things to be freed */
  if (seq != NULL)
    {
      set_free (seq);
    }

  return (NO_ERROR);
}

/*
 * db_set_filter() - This function causes set elements containing references
 *    to deleted objects to be completely removed from the designated set.
 *    The system does not automatically filter sets containing references to
 *    deleted objects; if you do not call this function, make sure that your
 *    application code is prepared to encounter deleted objects.
 * return : error code
 * set(in): set to filter
 */
int
db_set_filter (DB_SET * set)
{
  int retval;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (set);

  /*  Check if modifications are disabled only if the set is owned */
  if (set->owner != NULL)
    {
      CHECK_MODIFICATION_ERROR ();
    }

  retval = (set_filter (set));

  return (retval);
}

/*
 * db_seq_filter() - This function causes set elements containing references
 *    to deleted objects to be completely removed from the designated set.
 *    The system does not automatically filter sets containing references to
 *    deleted objects; if you do not call this function, make sure that your
 *    application code is prepared to encounter deleted objects.
 * return : error code
 * set(in): set to filter
 */
int
db_seq_filter (DB_SET * set)
{
  int retval;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (set);

  /*  Check if modifications are disabled only if the set is owned */
  if (set->owner != NULL)
    {
      CHECK_MODIFICATION_ERROR ();
    }

  retval = (set_filter (set));

  return (retval);
}

/*
 * db_set_copy() - This function makes a copy of the given set. The copied set
 *    will be a free set that is not owned by an object and will not be
 *    persistent in the database.
 * return : a new set
 * source(in): a set to copy
 *
 * note : To make the set persistent, you must assign it as the value of an
 *   object attribute. If you do not assign the set to an attribute, it must be
 *   freed manually with db_set_free function
 */
DB_SET *
db_set_copy (DB_SET * source)
{
  DB_SET *new_ = NULL;

  CHECK_CONNECT_NULL ();

  if (source != NULL)
    {
      new_ = set_copy (source);
    }

  return (new_);
}

/*
 * db_seq_copy() - This function makes a copy of the given source. The copied
 *    sequence will be a free set that is not owned by an object and will not
 *    be persistent in the database.
 * return : a new sequence
 * source(in): a sequence to copy
 *
 * note : To make the sequence persistent, you must assign it as the value of
 *   an object attribute. If you do not assign the sequence to an attribute,
 *   it must be freed manually with db_seq_free function
 */
DB_SEQ *
db_seq_copy (DB_SEQ * source)
{
  DB_SEQ *new_ = NULL;

  CHECK_CONNECT_NULL ();

  if (source != NULL)
    {
      new_ = set_copy (source);
    }

  return (new_);
}

/*
 *  SET/MULTI-SET FUNCTIONS
 */

/*
 * db_set_add() - This function adds an element to a set or multiset. If the
 *    set is a basic set and the value already exists in the set, a zero is
 *    returned, indicating that no error occurred. If the set is a multiset,
 *    the value will be added even if it already exists. If the set has been
 *    assigned as the value of an attribute, the domain of the value is first
 *    checked against the attribute domain. If they do not match, a zero is
 *    returned, indicating that no error occurred.
 * return : error code
 * set(in): set descriptor
 * value(in): value to add
 *
 * note : you may not make any assumptions about the position of the value
 *    within the set; it will be added wherever the system determines is most
 *    convenient. If you need to have sets with ordered elements, you must use
 *    sequences. Sets and multi-sets cannot contain NULL elements, if the value
 *    has a basic type of DB_TYPE_NULL, an error is returned.
 */
int
db_set_add (DB_SET * set, DB_VALUE * value)
{
  int error = NO_ERROR;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (set);

  /*  Check if modifications are disabled only if the set is owned */
  if (set->owner != NULL)
    {
      CHECK_MODIFICATION_ERROR ();
    }

  if ((value != NULL) && (DB_VALUE_TYPE (value) > DB_TYPE_LAST))
    {
      ERROR_SET (error, ER_OBJ_INVALID_ARGUMENTS);
    }
  else
    {
      error = set_add_element (set, value);
    }

  return (error);
}

/*
 * db_set_get() - This function gets the value of an element of a set or
 *    multiset. This is the only set or multiset function that accepts an
 *    index. The first element of the set is accessed with an index of 0
 *    (zero). The index is used to sequentially retrieve elements and assumes
 *    that the set will not be modified for the duration of the set iteration
 *    loop. You cannot assume that the elements of the set remain in any
 *    particular order after a db_set_add statement.
 * return : error code
 * set(in): set descriptor
 * index(in) : element index
 * value(out): value container to be filled in with element value
 *
 * note : The supplied value container is filled in with a copy of the set
 *    element contents and must be freed with db_value_clear or db_value_free
 *    when the value is no longer required.
 */
int
db_set_get (DB_SET * set, int index, DB_VALUE * value)
{
  int retval;

  CHECK_CONNECT_ERROR ();
  CHECK_2ARGS_ERROR (set, value);

  retval = (set_get_element (set, index, value));

  return (retval);
}

/*
 * db_set_drop() - This function removes the first matching element from a set
 *    or multiset. If no element matches the supplied value, a zero is
 *    returned, indicating no error occurred. If more than one element matches,
 *    only the first one is removed.
 * return : error code
 * set(in): set descriptor
 * value(in): value to drop from the set
 */
int
db_set_drop (DB_SET * set, DB_VALUE * value)
{
  int retval;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (set);

  /*  Check if modifications are disabled only if the set is owned */
  if (set->owner != NULL)
    {
      CHECK_MODIFICATION_ERROR ();
    }

  retval = (set_drop_element (set, value, false));

  return (retval);
}

/*
 * db_set_size() - This function returns the total number of elements in a set,
 *    including elements that may have a NULL value. This function should
 *    always be used prior to using program loops to iterate over the set
 *    elements.
 * return : total number of elements in the set
 * set(in): set descriptor
 */
int
db_set_size (DB_SET * set)
{
  int retval;

  CHECK_CONNECT_MINUSONE ();
  CHECK_1ARG_MINUSONE (set);

  /* allow all types */
  retval = (set_size (set));

  return (retval);
}

/*
 * db_set_cardinality() - The cardinality of a set is defined as the number of
 *    non-null elements, and this function returns the number of non-null
 *    elements in the set.
 * return : number of non-null elements in the set
 * set(in): set descriptor
 */
int
db_set_cardinality (DB_SET * set)
{
  int retval;

  CHECK_CONNECT_MINUSONE ();
  CHECK_1ARG_MINUSONE (set);

  /* allow all types */
  retval = (set_cardinality (set));

  return (retval);
}

/*
 * db_set_ismember() - This function checks to see if a value is found in a set
 * return : non-zero if the value is in the set
 * set(in): set descriptor
 * value(in): value to test
 */
int
db_set_ismember (DB_SET * set, DB_VALUE * value)
{
  int retval;

  CHECK_CONNECT_FALSE ();
  CHECK_1ARG_FALSE (set);

  /* allow all types */
  retval = (set_ismember (set, value)) ? 1 : 0;

  return (retval);
}

/*
 * db_set_isempty() - This function checks to see if a set is empty.  The set
 *    (or sequence) must have no elements at all in order for this to be true.
 *    If this is a sequence and there are only NULL elements, this function
 *    will return false since NULL elements are still considered valid elements
 *    for sequences.
 * return : non-zero if the set has no elements
 * set(in): set descriptor
 */
int
db_set_isempty (DB_SET * set)
{
  int retval;

  CHECK_CONNECT_FALSE ();
  CHECK_1ARG_FALSE (set);

  /* allow all types */
  retval = (set_isempty (set)) ? 1 : 0;

  return (retval);
}

/*
 * db_set_print() - This is a debugging function that prints a simple
 *    description of a set. This should be used for information purposes only.
 * return : error code
 * set(in): set descriptor
 */
int
db_set_print (DB_SET * set)
{
  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (set);

  /* allow all types */
  set_print (set);

  return (NO_ERROR);
}

/*
 * db_set_type() - This function returns the type identifier for a set. This
 *    can be used in places where it is not known if a set descriptor is for
 *    a set, multi-set or sequence.
 * return : set type identifier
 * set(in): set descriptor
 */
DB_TYPE
db_set_type (DB_SET * set)
{
  DB_TYPE type = DB_TYPE_NULL;

  if (set != NULL)
    {
      type = set_get_type (set);
    }

  return (type);
}

/*
 * SEQUENCE FUNCTIONS
 */

/*
 * db_seq_get() - This function retrieves the value of a sequence element.
 *    The first element of the sequence is accessed with an index of 0 (zero).
 * return : error code
 * set(in): sequence identifier
 * index(in): element index
 * value(out): value to be filled in with element contents
 *
 * note : The value will be copied from the sequence, so it must be released
 *    using either function db_value_clear() or db_value_free() when it is
 *    no longer needed.
 */
int
db_seq_get (DB_SET * set, int index, DB_VALUE * value)
{
  int retval;

  CHECK_CONNECT_ERROR ();
  CHECK_2ARGS_ERROR (set, value);

  /* should make sure this is a sequence, probably introduce another
     set_ level function to do this rather than checking the type here */
  retval = (set_get_element (set, index, value));

  return (retval);
}

/*
 * db_seq_put() - This function puts a value in a sequence at a fixed position.
 *    The value will always remain in the specified position so you can use
 *    db_seq_get() with the same index to retrieve it at a later time.
 *    This will overwrite the current value at that position if one exists. The
 *    value can be of type DB_TYPE_NULL, in which case the current element will
 *    be cleared and set to NULL.
 * return : error code
 * set(in): sequence descriptor
 * index(in): element index
 * value(in): value to put in the sequence
 *
 * note : The domain of the value must be compatible with the domain of the set
 *   (if the set has been assigned to an attribute). If the set does not have
 *   an element with the specified index, it will automatically grow to be as
 *   large as the given index. The empty elements (if any) between the former
 *   length of the set and the new index will be set to DB_TYPE_NULL.
 */
int
db_seq_put (DB_SET * set, int index, DB_VALUE * value)
{
  int error = NO_ERROR;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (set);

  /*  Check if modifications are disabled only if the set is owned */
  if (set->owner != NULL)
    {
      CHECK_MODIFICATION_ERROR ();
    }

  if ((value != NULL) && (DB_VALUE_TYPE (value) > DB_TYPE_LAST))
    {
      ERROR_SET (error, ER_OBJ_INVALID_ARGUMENTS);
    }
  else
    {
      error = set_put_element (set, index, value);
    }

  return (error);
}

/*
 * db_seq_insert() - This function inserts a value into a sequence at the
 *    specified position. All elements starting from that position will be
 *    shifted down to make room for the new element. As with other sequence
 *    building functions, the domain of the value must be compatible with the
 *    domain of the sequence. The sequence will automatically grow to make room
 *    for the new value. Any existing slots that contain NULL will not be
 *    reused because NULL is a valid sequence element. If the index is beyond
 *    the length of the sequence, the sequence will grow and the value is
 *    appended.
 * return : error code
 * set(in): sequence descriptor
 * index(in): element index
 * value(in): value to insert
 */
int
db_seq_insert (DB_SET * set, int index, DB_VALUE * value)
{
  int error = NO_ERROR;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (set);

  /*  Check if modifications are disabled only if the set is owned */
  if (set->owner != NULL)
    {
      CHECK_MODIFICATION_ERROR ();
    }

  if ((value != NULL) && (DB_VALUE_TYPE (value) > DB_TYPE_LAST))
    {
      ERROR_SET (error, ER_OBJ_INVALID_ARGUMENTS);
    }
  else
    {
      error = set_insert_element (set, index, value);
    }

  return (error);
}

/*
 * db_seq_drop() - This function removes an element from a sequence. Any
 *    elements following the indexed element will be shifted up to take up
 *    the space. The length of the set will be decreased by one.
 * return : error code
 * set(in): sequence descriptor
 * index(in): element index
 *
 * note : If you want to clear an element without shifting subsequent elements
 *   up, use db_seq_put() with a value of DB_TYPE_NULL.
 */
int
db_seq_drop (DB_SET * set, int index)
{
  int retval;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (set);

  /*  Check if modifications are disabled only if the set is owned */
  if (set->owner != NULL)
    {
      CHECK_MODIFICATION_ERROR ();
    }

  retval = (set_drop_seq_element (set, index));

  return (retval);
}

/*
 * db_seq_size() - This function returns the total number of slots allocated
 *    for a sequence.
 * return : total number of elements in the sequence
 * set(in): sequence descriptor
 */
int
db_seq_size (DB_SET * set)
{
  int retval;

  CHECK_CONNECT_MINUSONE ();
  CHECK_1ARG_MINUSONE (set);

  retval = (set_size (set));

  return (retval);
}

/*
 * db_seq_cardinality() - This function returns only the number of non-null
 *    elements in the sequence.
 * return : number of non-null elements in the sequence
 * set(in): sequence descriptor
 */
int
db_seq_cardinality (DB_SET * set)
{
  int retval;

  CHECK_CONNECT_MINUSONE ();
  CHECK_1ARG_MINUSONE (set);

  retval = (set_cardinality (set));

  return (retval);
}

/*
 * db_seq_print() - This is debug function to print out a simple description of
 *    a sequence.
 * return : error code
 * set(in): sequence descriptor
 */
int
db_seq_print (DB_SET * set)
{
  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (set);

  set_print (set);

  return (NO_ERROR);
}

/*
 * db_seq_find() - This function can be used to sequentially search for
 *    elements in a sequence that match a particular value. To search
 *    for duplicate elements in a sequence, you can call this function multiple
 *    times and set the index parameter to one greater than the number returned
 *    by the previous call to this function.
 * return: the index of the element or error code if not found
 * set(in): sequence descriptor
 * value(in): value to search for
 * index(in): starting index (zero if starting from the beginning)
 */
int
db_seq_find (DB_SET * set, DB_VALUE * value, int index)
{
  int retval;

  CHECK_CONNECT_ERROR ();
  CHECK_2ARGS_ERROR (set, value);

  retval = (set_find_seq_element (set, value, index));

  return (retval);
}

/*
 *  GENERIC COLLECTION FUNCTIONS
 */

/*
 * The new DB_COLLECTION style of collection maintenance is preferred over
 * the old SET/MULTISET/SEQUENCE distinction.  Its easier to code against
 * and makes switching between collection types easier.
 *
 */

/*
 * db_col_create() - This is the primary function for constructing a new
 *    collection. Type should be DB_TYPE_SET, DB_TYPE_MULTISET, or
 *    DB_TYPE_SEQUENCE. Size should always be specified if known before
 *    hand as it will significantly improve performance when incrementally
 *    building collections. Domain is optional. If NULL, it is assumed that
 *    this is a "wildcard" collection and can contain elements of any domain.
 *    The elements already existing in the collection should be within the
 *    supplied domain.If a domain is supplied, the type argument is ignored.
 * return : collection (NULL if error)
 * type(in): one of the DB_TYPE_ collection types
 * size(in): initial size to preallocate (zero if unknown)
 * domain(in): fully specified domain (optional)
 *
 * note : The collection handle returned must be freed using the db_col_free()
 *    function when the collection handle is no longer necessary.
 */
DB_COLLECTION *
db_col_create (DB_TYPE type, int size, DB_DOMAIN * domain)
{
  DB_COLLECTION *col;

  CHECK_CONNECT_NULL ();

  if (domain != NULL)
    {
      col = set_create_with_domain (domain, size);
    }
  else
    {
      col = set_create (type, size);
    }

  return col;
}

/*
 * db_col_copy() - This function returns a copy of the given collection. The
 *    new collection has the identical domain and element values as the source
 *    collection.
 * return : new collection (NULL if error)
 * col(in): collection to copy
 */
DB_COLLECTION *
db_col_copy (DB_COLLECTION * col)
{
  DB_COLLECTION *new_ = NULL;

  CHECK_CONNECT_NULL ();

  if (col != NULL)
    {
      new_ = set_copy (col);
    }

  return new_;
}

/*
 * db_col_free() - This function frees storage for the collection. This
 *    function must be called for any collection created by the
 *    db_col_create() function as well as for any collection handle
 *    returned by any other API function.
 * return : error status
 * col(in): collection to free
 */
int
db_col_free (DB_COLLECTION * col)
{
  /* don't check connection here, we always allow things to be freed */

  if (col != NULL)
    {
      set_free (col);
    }

  return NO_ERROR;
}

/*
 * db_col_filter() - This function examines each element in the collection for
 *   references to objects that have been deleted. If any such elements are
 *   found, they are removed. If the collection type is DB_TYPE_SEQUENCE, then
 *   the elements containing deleted object references will be changed to have
 *   a DB_TYPE_NULL value. DB_TYPE_MULTISET or DB_TYPE_SET, elements containing
 *   deleted object references are first converted to elements containing a
 *   DB_TYPE_NULL value. After all deleted object references have been
 *   converted to null values, all but one of the null values are then removed
 *   from the collection.
 * return : error status
 * col(in): collection to filter
 */
int
db_col_filter (DB_COLLECTION * col)
{
  int error;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (col);

  /*  Check if modifications are disabled only if the set is owned */
  if (col->owner != NULL)
    {
      CHECK_MODIFICATION_ERROR ();
    }

  error = set_filter (col);

  return error;
}

/*
 * db_col_add() -  This is used to add new elements to a collection.
 *
 * return : error status
 * col(in): collection to extend
 * value(in): value to add
 *
 * note : db_col_add is normally used only with collections of type DB_TYPE_SET
 *   and DB_TYPE_MULTISET.  It can be used with collections of type
 *   DB_TYPE_SEQUENCE but the new elements will always be appended to the end
 *   of the sequence.  If you need more control over the positioning of
 *   elements in a sequence, you may use the db_col_put or db_col_insert
 *   functions.
 */
int
db_col_add (DB_COLLECTION * col, DB_VALUE * value)
{
  int error = NO_ERROR;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (col);

  /*  Check if modifications are disabled only if the set is owned */
  if (col->owner != NULL)
    {
      CHECK_MODIFICATION_ERROR ();
    }

  error = set_add_element (col, value);

  return error;
}

/*
 * db_col_drop() - This function is used to remove a value from a collection.
 * return : error code
 * col(in): collection
 * value(in): value to drop
 * all(in): non-zero to drop all occurrences of the value
 *
 */
int
db_col_drop (DB_COLLECTION * col, DB_VALUE * value, int all)
{
  int error;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (col);

  /*  Check if modifications are disabled only if the set is owned */
  if (col->owner != NULL)
    {
      CHECK_MODIFICATION_ERROR ();
    }

  error = set_drop_element (col, value, false);

  return error;
}

/*
 * db_col_drop_element() - The element with the given index will be removed
 *    from the collection and all subsequent elements in the collection will
 *    be moved up. The sequence size will be reduced by one. If the sequence
 *    has no elements at the given index, an error is returned.
 * return : error code
 * col(in): collection
 * element_index(in): index of element to drop
 */
int
db_col_drop_element (DB_COLLECTION * col, int element_index)
{
  int error;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (col);

  /*  Check if modifications are disabled only if the set is owned */
  if (col->owner != NULL)
    {
      CHECK_MODIFICATION_ERROR ();
    }

  /* kludge, not preventing SET or MULTISET operations, might want to define
   * some behavior here, even thouth the resulting set order after the drop
   * is undefined.
   */
  error = set_drop_seq_element (col, element_index);

  return error;
}

/*
 * db_col_drop_nulls() - This function is used to remove all NULL db_values
 *    from a collection.
 * return : error code
 * col(in): collection
 */
int
db_col_drop_nulls (DB_COLLECTION * col)
{
  int error;
  DB_VALUE value;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (col);

  /*  Check if modifications are disabled only if the set is owned */
  if (col->owner != NULL)
    {
      CHECK_MODIFICATION_ERROR ();
    }

  PRIM_INIT_NULL (&value);

  error = set_drop_element (col, &value, true);

  return error;
}

/*
 * db_col_size() - This function is used to obtain the number of elements found
 *    within the collection.
 * return : number of elements in the collection
 * col(in): collection
 */
int
db_col_size (DB_COLLECTION * col)
{
  int size;

  CHECK_CONNECT_MINUSONE ();
  CHECK_1ARG_MINUSONE (col);

  size = set_size (col);

  return size;
}

/*
 * db_col_cardinality() - This function returns the number of elements in the
 *    collection that have non-NULL values.
 * return: the cardinality of the collection
 * col(in): collection
 *
 * note : This is different than db_col_size which returns the total
 *    number of elements in the collection, including those with NULL
 *    values. Use of db_col_cardinality is discouraged since it is
 *    almost always the case that the API programmer really should be
 *    using db_col_size.
 */
int
db_col_cardinality (DB_COLLECTION * col)
{
  int card;

  CHECK_CONNECT_MINUSONE ();
  CHECK_1ARG_MINUSONE (col);

  card = set_cardinality (col);

  return card;
}

/*
 * db_col_get() - This function is the primary function for retrieving values
 *    out of a collection. It can be used for collections of all types.
 * return : error status
 * col(in): collection
 * element_index(in): index of element to access
 * value(in): container in which to store value
 *
 * note : The insertion order of elements in a collection of type DB_TYPE_SET
 *    or DB_TYPE_MULTISET is undefined, but the collection is guaranteed to
 *    retain its current order as long as no modifications are made to the
 *    collection. This makes it possible to iterate over the elements of a
 *    collection using an index. Iterations over a collection are normally
 *    performed by first obtaining the size of the collection with the
 *    db_col_size() function and then entering a loop whose index begins at 0
 *    and ends at the value of db_col_size() minus one.
 */
int
db_col_get (DB_COLLECTION * col, int element_index, DB_VALUE * value)
{
  int error;

  CHECK_CONNECT_ERROR ();
  CHECK_2ARGS_ERROR (col, value);

  error = set_get_element (col, element_index, value);

  return error;
}

/*
 * db_col_put() - This function assigns the given value to the element of the
 *    collection with the given index. The previous contents of the element are
 *    freed. This function is normally used with collections of type
 *    DB_TYPE_SEQUENCE. This function is normally used to populate
 *    DB_TYPE_SEQUENCE collections since it allows more control over the
 *    positioning of the values within the collection. If the given collection
 *    is of type DB_TYPE_SET or DB_TYPE_MULTISET, the index argument is ignored
 *    and the function behaves identically to the db_col_add() function.
 * return : error code
 * col(in): collection
 * element_index(in): index of element to modify
 * value(in): value to assign
 */
int
db_col_put (DB_COLLECTION * col, int element_index, DB_VALUE * value)
{
  int error = NO_ERROR;
  DB_TYPE coltype;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (col);

  /*  Check if modifications are disabled only if the set is owned */
  if (col->owner != NULL)
    {
      CHECK_MODIFICATION_ERROR ();
    }

  coltype = set_get_type (col);
  if (coltype == DB_TYPE_SEQUENCE)
    {
      error = set_put_element (col, element_index, value);
    }
  else
    {
      error = set_add_element (col, value);
    }

  return error;
}

/*
 * db_col_insert() - This function inserts a new element into the sequence at a
 *    position immediately before the indexed element. This function is
 *    normally used with collections of type DB_TYPE_SEQUENCE. If the index is
 *    0, the new element is added to the beginning of the sequence. All
 *    elements in the sequence are moved down to make room for the new element.
 *    The sequence increases in size by one element.
 * return : error status
 * col(in): collection
 * element_index(in): index of new element
 * value(in): value to insert
 */
int
db_col_insert (DB_COLLECTION * col, int element_index, DB_VALUE * value)
{
  int error = NO_ERROR;
  DB_TYPE coltype;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (col);

  /*  Check if modifications are disabled only if the set is owned */
  if (col->owner != NULL)
    {
      CHECK_MODIFICATION_ERROR ();
    }

  coltype = set_get_type (col);
  if (coltype == DB_TYPE_SEQUENCE)
    {
      error = set_insert_element (col, element_index, value);
    }
  else
    {
      error = set_add_element (col, value);
    }

  return error;
}

/*
 * db_col_ismember() - This function can be used to determine if a particular
 *    value is found within a collection.
 * return :
 *      0 = the value was not found within the collection
 *     >0 = the value was found within the collection
 *     <0 = an error was detected.
 * col(in): collection
 * value(in): value to search for
 */
int
db_col_ismember (DB_COLLECTION * col, DB_VALUE * value)
{
  int member;

  CHECK_CONNECT_MINUSONE ();
  CHECK_1ARG_MINUSONE (col);

  member = set_ismember (col, value) ? 1 : 0;

  return member;
}

/*
 * db_col_find() - This function can be used to scan a collection looking for
 *    elements that whose values are equal to the given value. It is normally
 *    used with collections of DB_TYPE_SEQUENCE but can be used with other
 *    collection types.
 * return : index of the desired element
 * col(in): collection
 * value(in): value to search for
 * starting_index(in): starting index
 * found_index(out): returned index of element
 *
 * note : since this function uses element indexes, it can only be used
 *   reliably with collections of type DB_TYPE_SET and DB_TYPE_MULTISET when
 *   there are no modifications being made to the collection. The index
 *   returned by this function has the following properties. If the value is
 *   greater than or equal to 0, the number represents a valid element index.
 *   This index can be used with the db_col_get() or db_col_put()functions for
 *   example to directly access the element. If the index returned is -1, it
 *   indicates that the value as not found in the collection. When the element
 *   is not found, the error ER_SEQ_ELEMENT_NOT_FOUND is also set and returned.
 *   The index will also be -1 if any other error is detected.
 *   The starting_index parameter can be used to search for more than one
 *   occurrence of the value. To search for the first occurrence of the value,
 *   the starting_index should be 0. If an occurrence is found, to find the
 *   next occurrence, set the starting_index to the index value returned by
 *   the last db_col_find() call plus one.
 */
int
db_col_find (DB_COLLECTION * col, DB_VALUE * value,
	     int starting_index, int *found_index)
{
  int error = NO_ERROR;
  int psn;

  CHECK_CONNECT_ERROR ();
  CHECK_2ARGS_ERROR (col, value);

  psn = set_find_seq_element (col, value, starting_index);

  if (psn < 0)
    {
      if (found_index != NULL)
	{
	  *found_index = -1;
	}
      error = (int) psn;
    }
  else
    {
      if (found_index != NULL)
	{
	  *found_index = psn;
	}
    }

  return error;
}

/*
 * db_col_type() - This function returns the base type for this collection.
 * return : one of DB_TYPE_SET, DB_TYPE_MULTISET, or DB_TYPE_SEQUENCE.
 * col(in): collection
 */
DB_TYPE
db_col_type (DB_COLLECTION * col)
{
  DB_TYPE type = DB_TYPE_NULL;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR_WITH_TYPE (col, DB_TYPE);

  type = set_get_type (col);

  return type;
}

/*
 * db_col_domain() - This function returns the fully specified domain
 *    for the collection.
 * return : domain of the collection
 * col(in): collection
 */
DB_DOMAIN *
db_col_domain (DB_COLLECTION * col)
{
  DB_DOMAIN *domain = NULL;

  CHECK_CONNECT_NULL ();
  CHECK_1ARG_NULL (col);

  domain = set_get_domain (col);

  return domain;
}

/*
 * db_col_fprint() - This is a debugging function that will emit a printed
 *    representation of the collection to the file.
 *    It is not intended to be used to emit a "readable" representation of
 *    the collection, in particular, it may truncate the output if the
 *    collection is very long.
 * return : error code
 * fp(in): file handle
 * col(in): collection
 */
int
db_col_fprint (FILE * fp, DB_COLLECTION * col)
{
  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (col);

  /* hack, so we don't have to know how the compiler does this */
  if (fp == NULL)
    {
      fp = stdout;
    }

  set_fprint (fp, col);

  return NO_ERROR;
}

/*
 * db_col_print() - Please refer to the db_col_fprintf() function
 * return: error code
 * col(in): collection
 */
int
db_col_print (DB_COLLECTION * col)
{
  return db_col_fprint (stdout, col);
}

/*
 * db_col_optimize() - This funciton makes the set is in its optimal order
 *    for performance(sort overhead) reason.
 * return : error status
 * col(in): collection
 */
int
db_col_optimize (DB_COLLECTION * col)
{
  int error = NO_ERROR;

  CHECK_CONNECT_ERROR ();
  CHECK_1ARG_ERROR (col);

  error = set_optimize (col);

  return error;
}
