/*
 * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.index;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import org.h2.command.dml.AllColumnsForPlan;
import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.PageStoreTable;
import org.h2.table.TableFilter;
import org.h2.util.Utils;
import org.h2.value.DataType;
import org.h2.value.Value;

A non-unique index based on an in-memory hash map.
Author:Sergi Vladykin
/** * A non-unique index based on an in-memory hash map. * * @author Sergi Vladykin */
public class NonUniqueHashIndex extends BaseIndex {
The index of the indexed column.
/** * The index of the indexed column. */
private final int indexColumn; private final boolean totalOrdering; private Map<Value, ArrayList<Long>> rows; private final PageStoreTable tableData; private long rowCount; public NonUniqueHashIndex(PageStoreTable table, int id, String indexName, IndexColumn[] columns, IndexType indexType) { super(table, id, indexName, columns, indexType); Column column = columns[0].column; indexColumn = column.getColumnId(); totalOrdering = DataType.hasTotalOrdering(column.getType().getValueType()); tableData = table; reset(); } private void reset() { rows = totalOrdering ? new HashMap<Value, ArrayList<Long>>() : new TreeMap<Value, ArrayList<Long>>(database.getCompareMode()); rowCount = 0; } @Override public void truncate(Session session) { reset(); } @Override public void add(Session session, Row row) { Value key = row.getValue(indexColumn); ArrayList<Long> positions = rows.get(key); if (positions == null) { positions = Utils.newSmallArrayList(); rows.put(key, positions); } positions.add(row.getKey()); rowCount++; } @Override public void remove(Session session, Row row) { if (rowCount == 1) { // last row in table reset(); } else { Value key = row.getValue(indexColumn); ArrayList<Long> positions = rows.get(key); if (positions.size() == 1) { // last row with such key rows.remove(key); } else { positions.remove(row.getKey()); } rowCount--; } } @Override public Cursor find(Session session, SearchRow first, SearchRow last) { if (first == null || last == null) { throw DbException.throwInternalError(first + " " + last); } if (first != last) { if (compareKeys(first, last) != 0) { throw DbException.throwInternalError(); } } Value v = first.getValue(indexColumn); /* * Sometimes the incoming search is a similar, but not the same type * e.g. the search value is INT, but the index column is LONG. In which * case we need to convert, otherwise the HashMap will not find the * result. */ v = v.convertTo(tableData.getColumn(indexColumn).getType(), database.getMode(), null); ArrayList<Long> positions = rows.get(v); return new NonUniqueHashCursor(session, tableData, positions); } @Override public long getRowCount(Session session) { return rowCount; } @Override public long getRowCountApproximation() { return rowCount; } @Override public long getDiskSpaceUsed() { return 0; } @Override public void close(Session session) { // nothing to do } @Override public void remove(Session session) { // nothing to do } @Override public double getCost(Session session, int[] masks, TableFilter[] filters, int filter, SortOrder sortOrder, AllColumnsForPlan allColumnsSet) { for (Column column : columns) { int index = column.getColumnId(); int mask = masks[index]; if ((mask & IndexCondition.EQUALITY) != IndexCondition.EQUALITY) { return Long.MAX_VALUE; } } return 2; } @Override public void checkRename() { // ok } @Override public boolean needRebuild() { return true; } @Override public boolean canGetFirstOrLast() { return false; } @Override public Cursor findFirstOrLast(Session session, boolean first) { throw DbException.getUnsupportedException("HASH"); } @Override public boolean canScan() { return false; } }