/*
 * Copyright (c) 2010, Swedish Institute of Computer Science
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/**
 * \file
 *	Logic for relational databases.
 * \author
 * 	Nicolas Tsiftes <nvt@sics.se>
 */

#include <limits.h>
#include <string.h>

#include "lib/crc16.h"
#include "lib/list.h"
#include "lib/memb.h"

#define DEBUG DEBUG_NONE
#include "net/uip-debug.h"

#include "db-options.h"
#include "index.h"
#include "lvm.h"
#include "relation.h"
#include "result.h"
#include "storage.h"
#include "aql.h"

/*
 * The source_dest_map structure is used for mapping the pointers to 
 * data in a source row and in the corresponding destination row. The 
 * structure is calculated just before processing a relational 
 * selection, and then used to improve the performance when processing 
 * each row.
*/
struct source_dest_map {
  attribute_t *from_attr;
  attribute_t *to_attr;
  unsigned from_offset;
  unsigned to_offset;
};

static struct source_dest_map attr_map[AQL_ATTRIBUTE_LIMIT];

#if DB_FEATURE_JOIN
/*
 * The source_map structure is used for mapping attributes to
 * their offsets in rows.
 */
struct source_map {
  attribute_t *attr;
  unsigned char *from_ptr;
};

static struct source_map source_map[AQL_ATTRIBUTE_LIMIT];
#endif /* DB_FEATURE_JOIN */

static unsigned char row[DB_MAX_ATTRIBUTES_PER_RELATION * DB_MAX_ELEMENT_SIZE];
static unsigned char extra_row[DB_MAX_ATTRIBUTES_PER_RELATION * DB_MAX_ELEMENT_SIZE];
static unsigned char result_row[AQL_ATTRIBUTE_LIMIT * DB_MAX_ELEMENT_SIZE];
static unsigned char * const left_row = row;
static unsigned char * const right_row = extra_row;
static unsigned char * const join_row = result_row;

LIST(relations);
MEMB(relations_memb, relation_t, DB_RELATION_POOL_SIZE);
MEMB(attributes_memb, attribute_t, DB_ATTRIBUTE_POOL_SIZE);

static relation_t *relation_find(char *);
static attribute_t *attribute_find(relation_t *, char *);
static int get_attribute_value_offset(relation_t *, attribute_t *);
static void attribute_free(relation_t *, attribute_t *);
static void purge_relations(void);
static void relation_clear(relation_t *);
static relation_t *relation_allocate(void);
static void relation_free(relation_t *);

static relation_t *
relation_find(char *name)
{
  relation_t *rel;

  for(rel = list_head(relations); rel != NULL; rel = rel->next) {
    if(strcmp(rel->name, name) == 0) {
      return rel;
    }
  }

  return NULL;
}

static attribute_t *
attribute_find(relation_t *rel, char *name)
{
  attribute_t *attr;

  for(attr = list_head(rel->attributes); attr != NULL; attr = attr->next) {
    if(strcmp(attr->name, name) == 0) {
      return attr;
    }
  }
    return NULL;
}

static int
get_attribute_value_offset(relation_t *rel, attribute_t *attr)
{
  attribute_t *ptr;
  int offset;

  for(offset = 0, ptr = list_head(rel->attributes);
      ptr != NULL;
      ptr = ptr->next) {
    if(ptr == attr) {
      return offset;
    }
    offset += ptr->element_size;
  }

  return -1;
}

static void
attribute_free(relation_t *rel, attribute_t *attr)
{
  if(attr->index != NULL) {
    index_release(attr->index);
  }
  memb_free(&attributes_memb, attr);
  rel->attribute_count--;
}

static void
purge_relations(void)
{
  relation_t *rel;
  relation_t *next;

  for(rel = list_head(relations); rel != NULL;) {
    next = rel->next;
    if(rel->references == 0) {
      relation_free(rel);
    }
    rel = next;
  }
}

static void
relation_clear(relation_t *rel)
{
  memset(rel, 0, sizeof(*rel));
  rel->tuple_storage = -1;
  rel->cardinality = INVALID_TUPLE;
  rel->dir = DB_STORAGE;
  LIST_STRUCT_INIT(rel, attributes);
}

static relation_t *
relation_allocate(void)
{
  relation_t *rel;

  rel = memb_alloc(&relations_memb);
  if(rel == NULL) {
    purge_relations();
    rel = memb_alloc(&relations_memb);
    if(rel == NULL) {
      PRINTF("DB: Failed to allocate a relation\n");
      return NULL;
    }
  }

  relation_clear(rel);
  return rel;
}

static void
relation_free(relation_t *rel)
{
  attribute_t *attr;

  while((attr = list_pop(rel->attributes)) != NULL) {
    attribute_free(rel, attr);
  }

  list_remove(relations, rel);
  memb_free(&relations_memb, rel);
}

db_result_t
relation_init(void)
{
  list_init(relations);
  memb_init(&relations_memb);
  memb_init(&attributes_memb);

  return DB_OK;
}

relation_t *
relation_load(char *name)
{
  relation_t *rel;

  rel = relation_find(name);
  if(rel != NULL) {
    rel->references++;
    goto end;
  }

  rel = relation_allocate();
  if(rel == NULL) {
    return NULL;
  }

  if(DB_ERROR(storage_get_relation(rel, name))) {
    memb_free(&relations_memb, rel);
    return NULL;
  }

  memcpy(rel->name, name, sizeof(rel->name));
  rel->name[sizeof(rel->name) - 1] = '\0';
  rel->references = 1;
  list_add(relations, rel);

end:
  if(rel->dir == DB_STORAGE && DB_ERROR(storage_load(rel))) {
    relation_release(rel);
    return NULL;
  }

  return rel;
}

db_result_t
relation_release(relation_t *rel)
{
  if(rel->references > 0) {
    rel->references--;
  }

  if(rel->references == 0) {
    storage_unload(rel);
  }

  return DB_OK;
}

relation_t *
relation_create(char *name, db_direction_t dir)
{
  relation_t old_rel;
  relation_t *rel;

  if(*name != '\0') {
    relation_clear(&old_rel);

    if(storage_get_relation(&old_rel, name) == DB_OK) {
      /* Reject a creation request if the relation already exists. */
      PRINTF("DB: Attempted to create a relation that already exists (%s)\n",
             name);
      return NULL;
    }

    rel = relation_allocate();
    if(rel == NULL) {
      return NULL;
    }

    rel->cardinality = 0;

    strncpy(rel->name, name, sizeof(rel->name) - 1);
    rel->name[sizeof(rel->name) - 1] = '\0';
    rel->dir = dir;

    if(dir == DB_STORAGE) {
      storage_drop_relation(rel, 1);

      if(storage_put_relation(rel) == DB_OK) {
        list_add(relations, rel);
        return rel;
      }
      memb_free(&relations_memb, rel);
    } else {
      list_add(relations, rel);
      return rel;
    }
  }

  return NULL;
}

#if DB_FEATURE_REMOVE
db_result_t
relation_rename(char *old_name, char *new_name)
{
  if(DB_ERROR(relation_remove(new_name, 0)) ||
     DB_ERROR(storage_rename_relation(old_name, new_name))) {
    return DB_STORAGE_ERROR;
  }

  return DB_OK;
}
#endif /* DB_FEATURE_REMOVE */

attribute_t *
relation_attribute_add(relation_t *rel, db_direction_t dir, char *name,
		       domain_t domain, size_t element_size)
{
  attribute_t *attribute;
  tuple_id_t cardinality;

  cardinality = relation_cardinality(rel);
  if(cardinality != INVALID_TUPLE && cardinality > 0) {
    PRINTF("DB: Attempt to create an attribute in a non-empty relation\n");
    return NULL;
  }

  if(element_size == 0 || element_size > DB_MAX_ELEMENT_SIZE) {
    PRINTF("DB: Unacceptable element size: %u\n", element_size);
    return NULL;
  }

  attribute = memb_alloc(&attributes_memb);
  if(attribute == NULL) {
    PRINTF("DB: Failed to allocate attribute \"%s\"!\n", name);
    return NULL;
  }

  strncpy(attribute->name, name, sizeof(attribute->name) - 1);
  attribute->name[sizeof(attribute->name) - 1] = '\0';
  attribute->domain = domain;
  attribute->element_size = element_size;
  attribute->aggregator = 0;
  attribute->index = NULL;
  attribute->flags = 0 /*ATTRIBUTE_FLAG_UNIQUE*/;

  rel->row_length += element_size;

  list_add(rel->attributes, attribute);
  rel->attribute_count++;

  if(dir == DB_STORAGE) {
    if(DB_ERROR(storage_put_attribute(rel, attribute))) {
       PRINTF("DB: Failed to store attribute %s\n", attribute->name);
       memb_free(&attributes_memb, attribute);
       return NULL;
    }
  } else {
    index_load(rel, attribute);
  }

  return attribute;
}

attribute_t *
relation_attribute_get(relation_t *rel, char *name)
{
  attribute_t *attr;

  attr = attribute_find(rel, name);
  if(attr != NULL && !(attr->flags & ATTRIBUTE_FLAG_INVALID)) {
    return attr;
  }

  return NULL;
}

db_result_t
relation_attribute_remove(relation_t *rel, char *name)
{
  /* Not implemented completely. */
  return DB_IMPLEMENTATION_ERROR;
#if 0
  attribute_t *attr;

  if(rel->references > 1) {
    return DB_BUSY_ERROR;
  }

  attr = relation_attribute_get(rel, name);
  if(attr == NULL) {
    return DB_NAME_ERROR;
  }

  list_remove(rel->attributes, attr);
  attribute_free(rel, attr);
  return DB_OK;
#endif
}

db_result_t
relation_get_value(relation_t *rel, attribute_t *attr,
                  unsigned char *row_ptr, attribute_value_t *value)
{
  int offset;
  unsigned char *from_ptr;

  offset = get_attribute_value_offset(rel, attr);
  if(offset < 0) {
    return DB_IMPLEMENTATION_ERROR;
  }
  from_ptr = row_ptr + offset;

  return db_phy_to_value(value, attr, from_ptr);
}

db_result_t
relation_set_primary_key(relation_t *rel, char *name)
{
  attribute_t *attribute;

  attribute = relation_attribute_get(rel, name);
  if(attribute == NULL) {
    return DB_NAME_ERROR;
  }

  attribute->flags = ATTRIBUTE_FLAG_PRIMARY_KEY;
  PRINTF("DB: New primary key: %s\n", attribute->name);

  return DB_OK;
}

db_result_t
relation_remove(char *name, int remove_tuples)
{
  relation_t *rel;
  db_result_t result;

  rel = relation_load(name);
  if(rel == NULL) {
    /*
     * Attempt to remove an inexistent relation. To allow for this 
     * operation to be used for setting up repeatable tests and 
     * experiments, we do not signal an error.
     */
    return DB_OK;
  }

  if(rel->references > 1) {
    return DB_BUSY_ERROR;
  }

  result = storage_drop_relation(rel, remove_tuples);
  relation_free(rel);
  return result;
}

db_result_t
relation_insert(relation_t *rel, attribute_value_t *values)
{
  attribute_t *attr;
  unsigned char record[rel->row_length];
  unsigned char *ptr;
  attribute_value_t *value;
  db_result_t result;

  value = values;

  PRINTF("DB: Relation %s has a record size of %u bytes\n",
	 rel->name, (unsigned)rel->row_length);
  ptr = record;

  PRINTF("DB: Insert (");

  for(attr = list_head(rel->attributes); attr != NULL; attr = attr->next, value++) {
    /* Verify that the value is in the expected domain. An exception
       to this rule is that INT may be promoted to LONG. */
    if(attr->domain != value->domain &&
       !(attr->domain == DOMAIN_LONG && value->domain == DOMAIN_INT)) {
      PRINTF("DB: The value domain %d does not match the domain %d of attribute %s\n",
             value->domain, attr->domain, attr->name);
      return DB_RELATIONAL_ERROR;
    }

    /* Set the data area for removed attributes to 0. */
    if(attr->flags & ATTRIBUTE_FLAG_INVALID) {
      memset(ptr, 0, attr->element_size);
      ptr += attr->element_size;
      continue;
    }

    result = db_value_to_phy((unsigned char *)ptr, attr, value);
    if(DB_ERROR(result)) {
      return result;
    }

#if DEBUG
    switch(attr->domain) {
    case DOMAIN_INT:
      PRINTF("%s=%d", attr->name, VALUE_INT(value));
      break;
    case DOMAIN_LONG:
      PRINTF("%s=%ld", attr->name, VALUE_LONG(value));
      break;
    case DOMAIN_STRING:
      PRINTF("%s='%s", attr->name, VALUE_STRING(value));
      break;
    default:
      PRINTF(")\nDB: Unhandled attribute domain: %d\n", attr->domain);
      return DB_TYPE_ERROR;
    }

    if(attr->next != NULL) {
      PRINTF(", ");
    }
#endif /* DEBUG */

    ptr += attr->element_size;
    if(attr->index != NULL) {
      if(DB_ERROR(index_insert(attr->index, value, rel->next_row))) {
        return DB_INDEX_ERROR;
      }
    }
  }

  PRINTF(")\n");

  rel->cardinality++;
  rel->next_row++;
  return storage_put_row(rel, record);
}

static void
aggregate(attribute_t *attr, attribute_value_t *value)
{
  long long_value;

  switch(value->domain) {
  case DOMAIN_INT:
    long_value = VALUE_INT(value);
    break;
  case DOMAIN_LONG:
    long_value = VALUE_LONG(value);
    break;
  default:
    return;
  }

  switch(attr->aggregator) {
  case AQL_COUNT:
    attr->aggregation_value++;
    break;
  case AQL_SUM:
    attr->aggregation_value += long_value;
    break;
  case AQL_MEAN:
    break;
  case AQL_MEDIAN:
    break;
  case AQL_MAX:
    if(long_value > attr->aggregation_value) {
      attr->aggregation_value = long_value;
    }
    break;
  case AQL_MIN:
    if(long_value < attr->aggregation_value) {
      attr->aggregation_value = long_value;
    }
    break;
  default:
    break;
  }
}

static db_result_t
generate_attribute_map(struct source_dest_map *attr_map, unsigned attribute_count,
                       relation_t *from_rel, relation_t *to_rel, 
                       unsigned char *from_row, unsigned char *to_row)
{
  attribute_t *from_attr;
  attribute_t *to_attr;
  unsigned size_sum;
  struct source_dest_map *attr_map_ptr;
  int offset;

  attr_map_ptr = attr_map;
  for(size_sum = 0, to_attr = list_head(to_rel->attributes);
      to_attr != NULL;
      to_attr = to_attr->next) {
    from_attr = attribute_find(from_rel, to_attr->name);
    if(from_attr == NULL) {
       PRINTF("DB: Invalid attribute in the result relation: %s\n",
		to_attr->name);
      return DB_NAME_ERROR;
    }

    attr_map_ptr->from_attr = from_attr;
    attr_map_ptr->to_attr = to_attr;
    offset = get_attribute_value_offset(from_rel, from_attr);
    if(offset < 0) {
      return DB_IMPLEMENTATION_ERROR;
    }
    attr_map_ptr->from_offset = offset;
    attr_map_ptr->to_offset = size_sum;

    size_sum += to_attr->element_size;
    attr_map_ptr++;
  }

  return DB_OK;
}

static void
select_index(db_handle_t *handle, lvm_instance_t *lvm_instance)
{
  index_t *index;
  attribute_t *attr;
  operand_value_t min;
  operand_value_t max;
  attribute_value_t av_min;
  attribute_value_t av_max;
  long range;
  unsigned long min_range;

  index = NULL;
  min_range = ULONG_MAX;

  /* Find all indexed and derived attributes, and select the index of 
     the attribute with the smallest range. */
  for(attr = list_head(handle->rel->attributes);
      attr != NULL;
      attr = attr->next) {
    if(attr->index != NULL &&
       !LVM_ERROR(lvm_get_derived_range(lvm_instance, attr->name, &min, &max))) {
      range = (unsigned long)max.l - (unsigned long)min.l;
      PRINTF("DB: The search range for attribute \"%s\" comprises %ld values\n",
             attr->name, range + 1);

      if(range <= min_range) {
        index = attr->index;
        av_min.domain = av_max.domain = DOMAIN_INT;
        VALUE_LONG(&av_min) = min.l;
        VALUE_LONG(&av_max) = max.l;
      }
    }
  }

  if(index != NULL) {
    /* We found a suitable index; get an iterator for it. */
    if(index_get_iterator(&handle->index_iterator, index, 
                          &av_min, &av_max) == DB_OK) {
      handle->flags |= DB_HANDLE_FLAG_SEARCH_INDEX;
    }
  }
}

static db_result_t
generate_selection_result(db_handle_t *handle, relation_t *rel, aql_adt_t *adt)
{
  relation_t *result_rel;
  unsigned attribute_count;
  attribute_t *attr;

  result_rel = handle->result_rel;

  handle->current_row = 0;
  handle->ncolumns = 0;
  handle->tuple_id = 0;
  for(attr = list_head(result_rel->attributes); attr != NULL; attr = attr->next) {
    if(attr->flags & ATTRIBUTE_FLAG_NO_STORE) {
      continue;
    }
    handle->ncolumns++;
  }
  handle->tuple = (tuple_t)result_row;

  attribute_count = result_rel->attribute_count;
  if(DB_ERROR(generate_attribute_map(attr_map, attribute_count, rel, result_rel, row, result_row))) {
    return DB_IMPLEMENTATION_ERROR;
  }

  if(adt->lvm_instance != NULL) {
    /* Try to establish acceptable ranges for the attribute values. */
    if(!LVM_ERROR(lvm_derive(adt->lvm_instance))) {
      select_index(handle, adt->lvm_instance);
    }
  }

  handle->flags |= DB_HANDLE_FLAG_PROCESSING;

  return DB_OK;
}

#if DB_FEATURE_REMOVE
db_result_t
relation_process_remove(void *handle_ptr)
{
  db_handle_t *handle;
  aql_adt_t *adt;
  db_result_t result;

  handle = (db_handle_t *)handle_ptr;
  adt = handle->adt;

  result = relation_process_select(handle_ptr);
  if(result == DB_FINISHED) {
    PRINTF("DB: Finished removing tuples. Overwriting relation %s with the result\n", 
	adt->relations[1]);
    relation_release(handle->rel);
    relation_rename(adt->relations[0], adt->relations[1]);
  }

  return result;
}
#endif

db_result_t
relation_process_select(void *handle_ptr)
{
  db_handle_t *handle;
  aql_adt_t *adt;
  db_result_t result;
  unsigned attribute_count;
  struct source_dest_map *attr_map_ptr, *attr_map_end;
  attribute_t *result_attr;
  unsigned char *from_ptr;
  unsigned char *to_ptr;
  operand_value_t operand_value;
  uint8_t intbuf[2];
  attribute_value_t value;
  lvm_status_t wanted_result;

  handle = (db_handle_t *)handle_ptr;
  adt = (aql_adt_t *)handle->adt;

  attribute_count = handle->result_rel->attribute_count;
  attr_map_end = attr_map + attribute_count;

  if(handle->flags & DB_HANDLE_FLAG_SEARCH_INDEX) {
    handle->tuple_id = index_get_next(&handle->index_iterator);
    if(handle->tuple_id == INVALID_TUPLE) {
      PRINTF("DB: An attribute value could not be found in the index\n");
      if(handle->index_iterator.next_item_no == 0) {
        return DB_INDEX_ERROR;
      }

      if(adt->flags & AQL_FLAG_AGGREGATE) {
        goto end_aggregation;
      }

      return DB_FINISHED;
    }
  }

  /* Put the tuples fulfilling the given condition into a new relation.
     The tuples may be projected. */
  result = storage_get_row(handle->rel, &handle->tuple_id, row);
  handle->tuple_id++;
  if(DB_ERROR(result)) {
    PRINTF("DB: Failed to get a row in relation %s!\n", handle->rel->name);
    return result;
  } else if(result == DB_FINISHED) {
    if(AQL_GET_FLAGS(adt) & AQL_FLAG_AGGREGATE) {
      goto end_aggregation;
    }
    return DB_FINISHED;
  }

  /* Process the attributes in the result relation. */
  for(attr_map_ptr = attr_map; attr_map_ptr < attr_map_end; attr_map_ptr++) {
    from_ptr = row + attr_map_ptr->from_offset;
    result_attr = attr_map_ptr->to_attr;

    /* Update the internal state of the PLE. */
    if(result_attr->domain == DOMAIN_INT) {
      operand_value.l = from_ptr[0] << 8 | from_ptr[1];
      lvm_set_variable_value(result_attr->name, operand_value);
    } else if(result_attr->domain == DOMAIN_LONG) {
      operand_value.l = (uint32_t)from_ptr[0] << 24 |
                        (uint32_t)from_ptr[1] << 16 |
                        (uint32_t)from_ptr[2] << 8 |
                        from_ptr[3];
      lvm_set_variable_value(result_attr->name, operand_value);
    }

    if(result_attr->flags & ATTRIBUTE_FLAG_NO_STORE) {
      /* The attribute is used just for the predicate,
         so do not copy the current value into the result. */
      continue;
    }

    if(!(AQL_GET_FLAGS(adt) & AQL_FLAG_AGGREGATE)) {
      /* No aggregators. Copy the original value into the resulting tuple. */
      memcpy(result_row + attr_map_ptr->to_offset, from_ptr,
             result_attr->element_size);
    }
  }

  wanted_result = TRUE;
  if(AQL_GET_FLAGS(adt) & AQL_FLAG_INVERSE_LOGIC) {
    wanted_result = FALSE;
  }

  /* Check whether the given predicate is true for this tuple. */
  if(adt->lvm_instance == NULL ||
     lvm_execute(adt->lvm_instance) == wanted_result) {
    if(AQL_GET_FLAGS(adt) & AQL_FLAG_AGGREGATE) {
      for(attr_map_ptr = attr_map; attr_map_ptr < attr_map_end; attr_map_ptr++) {
        from_ptr = row + attr_map_ptr->from_offset;
        result = db_phy_to_value(&value, attr_map_ptr->to_attr, from_ptr);
        if(DB_ERROR(result)) {
	  return result;
        }
        aggregate(attr_map_ptr->to_attr, &value);
      }
    } else {
      if(AQL_GET_FLAGS(adt) & AQL_FLAG_ASSIGN) {
        if(DB_ERROR(storage_put_row(handle->result_rel, result_row))) {
          PRINTF("DB: Failed to store a row in the result relation!\n");
          return DB_STORAGE_ERROR;
        }
      }
      handle->current_row++;
      return DB_GOT_ROW;
    }
  }

  return DB_OK;

end_aggregation:
  /* Generate aggregated result if requested. */
  for(attr_map_ptr = attr_map; attr_map_ptr < attr_map_end; attr_map_ptr++) {
    result_attr = attr_map_ptr->to_attr;
    to_ptr = result_row + attr_map_ptr->to_offset;

    intbuf[0] = result_attr->aggregation_value >> 8;
    intbuf[1] = result_attr->aggregation_value & 0xff;
    from_ptr = intbuf;
    memcpy(to_ptr, from_ptr, result_attr->element_size);
  }

  if(AQL_GET_FLAGS(adt) & AQL_FLAG_ASSIGN) {
    if(DB_ERROR(storage_put_row(handle->result_rel, result_row))) {
      PRINTF("DB: Failed to store a row in the result relation!\n");
      return DB_STORAGE_ERROR;
    }
  }

  handle->current_row = 1;
  AQL_GET_FLAGS(adt) &= ~AQL_FLAG_AGGREGATE; /* Stop the aggregation. */

  return DB_GOT_ROW;
}

db_result_t
relation_select(void *handle_ptr, relation_t *rel, void *adt_ptr)
{
  aql_adt_t *adt;
  db_handle_t *handle;
  char *name;
  db_direction_t dir;
  char *attribute_name;
  attribute_t *attr;
  int i;
  int normal_attributes;

  adt = (aql_adt_t *)adt_ptr;

  handle = (db_handle_t *)handle_ptr;
  handle->rel = rel;
  handle->adt = adt;

  if(AQL_GET_FLAGS(adt) & AQL_FLAG_ASSIGN) {
    name = adt->relations[0];
    dir = DB_STORAGE;
  } else {
    name = RESULT_RELATION;
    dir = DB_MEMORY;
  }
  relation_remove(name, 1);
  relation_create(name, dir);
  handle->result_rel = relation_load(name);

  if(handle->result_rel == NULL) {
    PRINTF("DB: Failed to load a relation for the query result\n");
    return DB_ALLOCATION_ERROR;
  }

  for(i = normal_attributes = 0; i < AQL_ATTRIBUTE_COUNT(adt); i++) {
    attribute_name = adt->attributes[i].name;

    attr = relation_attribute_get(rel, attribute_name);
    if(attr == NULL) {
      PRINTF("DB: Select for invalid attribute %s in relation %s!\n",
	     attribute_name, rel->name);
      return DB_NAME_ERROR;
    }

    PRINTF("DB: Found attribute %s in relation %s\n",
	attribute_name, rel->name);

    attr = relation_attribute_add(handle->result_rel, dir,
				  attribute_name, 
				  adt->aggregators[i] ? DOMAIN_INT : attr->domain,
				  attr->element_size);
    if(attr == NULL) {
      PRINTF("DB: Failed to add a result attribute\n");
      relation_release(handle->result_rel);
      return DB_ALLOCATION_ERROR;
    }

    attr->aggregator = adt->aggregators[i];
    switch(attr->aggregator) {
    case AQL_NONE:
      if(!(adt->attributes[i].flags & ATTRIBUTE_FLAG_NO_STORE)) {
        /* Only count attributes projected into the result set. */
        normal_attributes++;
      }
      break;
    case AQL_MAX:
      attr->aggregation_value = LONG_MIN;
      break;
    case AQL_MIN:
      attr->aggregation_value = LONG_MAX;
      break;
    default:
      attr->aggregation_value = 0;
      break;
    }

    attr->flags = adt->attributes[i].flags;
  }

  /* Preclude mixes of normal attributes and aggregated ones in 
     selection results. */
  if(normal_attributes > 0 &&
     handle->result_rel->attribute_count > normal_attributes) {
     return DB_RELATIONAL_ERROR;
  }

  return generate_selection_result(handle, rel, adt);
}

#if DB_FEATURE_JOIN
db_result_t
relation_process_join(void *handle_ptr)
{
  db_handle_t *handle;
  db_result_t result;
  relation_t *left_rel;
  relation_t *right_rel;
  relation_t *join_rel;
  unsigned char *join_next_attribute_ptr;
  size_t element_size;
  tuple_id_t right_tuple_id;
  attribute_value_t value;
  int i;

  handle = (db_handle_t *)handle_ptr;
  left_rel = handle->left_rel;
  right_rel = handle->right_rel;
  join_rel = handle->join_rel;

  if(!(handle->flags & DB_HANDLE_FLAG_INDEX_STEP)) {
    goto inner_loop;
  }

  /* Equi-join for indexed attributes only. In the outer loop, we iterate over
     each tuple in the left relation. */
  for(handle->tuple_id = 0;; handle->tuple_id++) {
    result = storage_get_row(left_rel, &handle->tuple_id, left_row);
    if(DB_ERROR(result)) {
      PRINTF("DB: Failed to get a row in left relation %s!\n", left_rel->name);
      return result;
    } else if(result == DB_FINISHED) {
      return DB_FINISHED;
    }

    if(DB_ERROR(relation_get_value(left_rel, handle->left_join_attr, left_row, &value))) {
      PRINTF("DB: Failed to get a value of the attribute \"%s\" to join on\n",
	handle->left_join_attr->name);
      return DB_IMPLEMENTATION_ERROR;
    }

    if(DB_ERROR(index_get_iterator(&handle->index_iterator, 
                                   handle->right_join_attr->index, 
                                   &value, &value))) { 
      PRINTF("DB: Failed to get an index iterator\n");
      return DB_INDEX_ERROR;
    }
    handle->flags &= ~DB_HANDLE_FLAG_INDEX_STEP;

    /* In the inner loop, we iterate over all rows with a matching value for the
       join attribute. The index component provides an iterator for this purpose. */
inner_loop:
    for(;;) {
      /* Get all rows matching the attribute value in the right relation. */
      right_tuple_id = index_get_next(&handle->index_iterator);
      if(right_tuple_id == INVALID_TUPLE) {
        /* Exclude this row from the left relation in the result,
           and step to the next value in the index iteration. */
        handle->flags |= DB_HANDLE_FLAG_INDEX_STEP;
        break;
      }

      result = storage_get_row(right_rel, &right_tuple_id, right_row);
      if(DB_ERROR(result)) {
        PRINTF("DB: Failed to get a row in right relation %s!\n", right_rel->name);
        return result;
      } else if(result == DB_FINISHED) {
	PRINTF("DB: The index refers to an invalid row: %lu\n",
	       (unsigned long)right_tuple_id);
        return DB_IMPLEMENTATION_ERROR;
      }

      /* Use the source attribute map to fill in the physical representation
	 of the resulting tuple. */
      join_next_attribute_ptr = join_row;

      for(i = 0; i < join_rel->attribute_count; i++) {
	element_size = source_map[i].attr->element_size;

	memcpy(join_next_attribute_ptr, source_map[i].from_ptr, element_size);
	join_next_attribute_ptr += element_size;
      }

      if(((aql_adt_t *)handle->adt)->flags & AQL_FLAG_ASSIGN) {
        if(DB_ERROR(storage_put_row(join_rel, join_row))) {
          return DB_STORAGE_ERROR;
        }
      }

      handle->current_row++;
      return DB_GOT_ROW;
    }
  }

  return DB_OK;
}

static db_result_t
generate_join_result(db_handle_t *handle)
{
  relation_t *left_rel;
  relation_t *right_rel;
  relation_t *join_rel;
  attribute_t *attr;
  attribute_t *result_attr;
  struct source_map *source_pair;
  int i;
  int offset;
  unsigned char *from_ptr;

  handle->tuple = (tuple_t)join_row;
  handle->tuple_id = 0;

  left_rel = handle->left_rel;
  right_rel = handle->right_rel;
  join_rel = handle->join_rel;

  /* Generate a map over the source attributes for each
     attribute in the join relation. */
  for(i = 0, result_attr = list_head(join_rel->attributes);
      result_attr != NULL; 
      result_attr = result_attr->next, i++) {
    source_pair = &source_map[i];
    attr = attribute_find(left_rel, result_attr->name);
    if(attr != NULL) {
      offset = get_attribute_value_offset(left_rel, attr);
      from_ptr = left_row + offset;
    } else if((attr = attribute_find(right_rel, result_attr->name)) != NULL) {
      offset = get_attribute_value_offset(right_rel, attr);
      from_ptr = right_row + offset;
    } else {
      PRINTF("DB: The attribute %s could not be found\n", result_attr->name);
      return DB_NAME_ERROR;
    }

    if(offset < 0) {
      PRINTF("DB: Unable to retrieve attribute values for the JOIN result\n");
      return DB_IMPLEMENTATION_ERROR;
    }
    
    source_pair->attr = attr;
    source_pair->from_ptr = from_ptr;
  }

  handle->flags |= DB_HANDLE_FLAG_PROCESSING;

  return DB_OK;
}

db_result_t
relation_join(void *query_result, void *adt_ptr)
{
  aql_adt_t *adt;
  db_handle_t *handle;
  relation_t *left_rel;
  relation_t *right_rel;
  relation_t *join_rel;
  char *name;
  db_direction_t dir;
  int i;
  char *attribute_name;
  attribute_t *attr;

  adt = (aql_adt_t *)adt_ptr;

  handle = (db_handle_t *)query_result;
  handle->current_row = 0;
  handle->ncolumns = 0;
  handle->adt = adt;
  handle->flags = DB_HANDLE_FLAG_INDEX_STEP;

  if(AQL_GET_FLAGS(adt) & AQL_FLAG_ASSIGN) {
    name = adt->relations[0];
    dir = DB_STORAGE;
  } else {
    name = RESULT_RELATION;
    dir = DB_MEMORY;
  }
  relation_remove(name, 1);
  relation_create(name, dir);
  join_rel = relation_load(name);
  handle->result_rel = join_rel;

  if(join_rel == NULL) {
    PRINTF("DB: Failed to create a join relation!\n");
    return DB_ALLOCATION_ERROR;
  }

  handle->join_rel = handle->result_rel = join_rel;
  left_rel = handle->left_rel;
  right_rel = handle->right_rel;

  handle->left_join_attr = relation_attribute_get(left_rel, adt->attributes[0].name);
  handle->right_join_attr = relation_attribute_get(right_rel, adt->attributes[0].name);
  if(handle->left_join_attr == NULL || handle->right_join_attr == NULL) {
    PRINTF("DB: The attribute (\"%s\") to join on does not exist in both relations\n",
	adt->attributes[0].name);
    return DB_RELATIONAL_ERROR;
  }

  if(!index_exists(handle->right_join_attr)) {
    PRINTF("DB: The attribute to join on is not indexed\n");
    return DB_INDEX_ERROR;
  }

  /*
   * Define the resulting relation. We start from 1 when counting attributes
   * because the first attribute is only the one to join, and is not included
   * by default in the projected attributes.
   */
  for(i = 1; i < AQL_ATTRIBUTE_COUNT(adt); i++) {
    attribute_name = adt->attributes[i].name;
    attr = relation_attribute_get(left_rel, attribute_name);
    if(attr == NULL) {
      attr = relation_attribute_get(right_rel, attribute_name);
      if(attr == NULL) {
	PRINTF("DB: The projection attribute \"%s\" does not exist in any of the relations to join\n",
		attribute_name);
        return DB_RELATIONAL_ERROR;
      }
    }

    if(relation_attribute_add(join_rel, dir, attr->name, attr->domain, 
                              attr->element_size) == NULL) {
      PRINTF("DB: Failed to add an attribute to the join relation\n");
      return DB_ALLOCATION_ERROR;
    }

    handle->ncolumns++;
  }

  return generate_join_result(handle);
}
#endif /* DB_FEATURE_JOIN */

tuple_id_t
relation_cardinality(relation_t *rel)
{
  tuple_id_t tuple_id;


  if(rel->cardinality != INVALID_TUPLE) {
    return rel->cardinality;
  }

  if(!RELATION_HAS_TUPLES(rel)) {
    return 0;
  }

  if(DB_ERROR(storage_get_row_amount(rel, &tuple_id))) {
    return INVALID_TUPLE;
  }

  rel->cardinality = tuple_id;

  PRINTF("DB: Relation %s has cardinality %lu\n", rel->name,
	(unsigned long)tuple_id);

  return tuple_id;
}
