/* * Copyright 2004-2014 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.HashSet; 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.RegularTable; import org.h2.table.TableFilter; import org.h2.util.New; import org.h2.util.ValueHashMap; import org.h2.value.Value; /** * 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. */ private final int indexColumn; private ValueHashMap<ArrayList<Long>> rows; private final RegularTable tableData; private long rowCount; public NonUniqueHashIndex(RegularTable table, int id, String indexName, IndexColumn[] columns, IndexType indexType) { initBaseIndex(table, id, indexName, columns, indexType); this.indexColumn = columns[0].column.getColumnId(); this.tableData = table; reset(); } private void reset() { rows = ValueHashMap.newInstance(); 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 = New.arrayList(); 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 ValueHashMap will not find the * result. */ v = v.convertTo(tableData.getColumn(indexColumn).getType()); 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, HashSet<Column> 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; } }