/** * H2GIS is a library that brings spatial support to the H2 Database Engine * <http://www.h2database.com>. H2GIS is developed by CNRS * <http://www.cnrs.fr/>. * * This code is part of the H2GIS project. H2GIS is free software; * you can redistribute it and/or modify it under the terms of the GNU * Lesser General Public License as published by the Free Software Foundation; * version 3.0 of the License. * * H2GIS 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 Lesser General Public License * for more details <http://www.gnu.org/licenses/>. * * * For more information, please consult: <http://www.h2gis.org/> * or contact directly: info_at_h2gis.org */ package org.h2gis.functions.io.file_table; import org.h2.api.ErrorCode; import org.h2.engine.Session; import org.h2.index.BaseIndex; import org.h2.index.Cursor; import org.h2.index.IndexCondition; import org.h2.index.IndexType; 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.Table; import org.h2.table.TableFilter; import org.h2.value.DataType; import org.h2.value.Value; import org.h2.value.ValueLong; import org.h2gis.functions.io.FileDriver; import java.io.IOException; import java.util.HashSet; /** * ScanIndex of {@link org.h2gis.drivers.FileDriver}, the key is the row index [1-n]. * @author Nicolas Fortin */ public class H2TableIndex extends BaseIndex { public static final String PK_COLUMN_NAME = "PK"; private FileDriver driver; private final boolean isScanIndex; /** * Constructor for scan index. Hidden column _ROWID_. * @param driver Linked file driver * @param table Linked table * @param id Index identifier */ public H2TableIndex(FileDriver driver, Table table, int id) { this.isScanIndex = true; this.driver = driver; IndexColumn indexColumn = new IndexColumn(); indexColumn.columnName = "key"; indexColumn.column = new Column("key", Value.LONG); initBaseIndex(table, id, table.getName() + "_ROWID_", new IndexColumn[]{indexColumn}, IndexType.createScan(true)); } /** * Constructor for primary key index. * @param driver Linked file driver * @param table Linked table * @param id Index identifier * @param PKColumn Primary key column declaration * @param indexName Unique index name */ public H2TableIndex(FileDriver driver, Table table, int id, Column PKColumn, String indexName) { this.isScanIndex = false; this.driver = driver; IndexColumn indexColumn = new IndexColumn(); indexColumn.columnName = PK_COLUMN_NAME; indexColumn.column = PKColumn; indexColumn.sortType = SortOrder.ASCENDING; initBaseIndex(table, id, indexName, new IndexColumn[]{indexColumn}, IndexType.createPrimaryKey(true, false)); } @Override public void checkRename() { // Nothing to check } public FileDriver getDriver() { return driver; } @Override public Row getRow(Session session, long key) { try { Object[] driverRow = driver.getRow(key - 1); Value[] values = new Value[driverRow.length + 1]; Column[] columns = table.getColumns(); values[0] = ValueLong.get(key); for(int idField=1;idField<=driverRow.length;idField++) { // TODO in H2, switch on type parameter instead of if elseif values[idField] = DataType.convertToValue(session, driverRow[idField - 1], columns[idField - 1].getType()); } Row row = session.createRow(values, Row.MEMORY_CALCULATE); row.setKey(key); return row; } catch (IOException ex) { throw DbException.get(ErrorCode.IO_EXCEPTION_1,ex); } } @Override public void close(Session session) { //To change body of implemented methods use File | Settings | File Templates. } @Override public void add(Session session, Row row) { //To change body of implemented methods use File | Settings | File Templates. } @Override public void remove(Session session, Row row) { throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1,"remove in Shape files"); } @Override public Cursor find(Session session, SearchRow first, SearchRow last) { if (!isScanIndex) { Row remakefirst = session.createRow(null, 0); if(first != null) { remakefirst.setKey(first.getValue(0).getLong()); } else { remakefirst.setKey(1); } Row remakeLast = session.createRow(null, 0); if(last != null) { remakeLast.setKey(last.getValue(0).getLong()); } else { remakeLast.setKey(getRowCount(session)); } first = remakefirst; last = remakeLast; } return new SHPCursor(this, first, last, session); } @Override public boolean canScan() { return true; } @Override public boolean canFindNext() { return true; } @Override public double getCost(Session session, int[] masks, TableFilter[] filter, int i, SortOrder so, HashSet<Column> hs) { if(masks == null) { return Double.MAX_VALUE; } for (Column column : columns) { int index = column.getColumnId(); int mask = masks[index]; if ((mask & IndexCondition.EQUALITY) != IndexCondition.EQUALITY && (mask & IndexCondition.START) != IndexCondition.START && (mask & IndexCondition.END) != IndexCondition.END && (mask & IndexCondition.RANGE) != IndexCondition.RANGE) { return Double.MAX_VALUE; } } return 2; } @Override public void remove(Session session) { throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1,"remove in Shape files"); } @Override public void truncate(Session session) { throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1,"truncate in Shape files"); } @Override public boolean canGetFirstOrLast() { return true; } @Override public Cursor findFirstOrLast(Session session, boolean first) { return new SHPCursor(this,first ? 0 : getRowCount(session),session); } @Override public boolean needRebuild() { return false; } @Override public long getRowCount(Session session) { return driver.getRowCount(); } @Override public long getRowCountApproximation() { return driver.getRowCount(); } @Override public long getDiskSpaceUsed() { return 0; } @Override public boolean isRowIdIndex() { return isScanIndex; } private static class SHPCursor implements Cursor { private H2TableIndex tIndex; private long rowIndex; private Session session; private SearchRow begin, end; private SHPCursor(H2TableIndex tIndex, long rowIndex, Session session) { this.tIndex = tIndex; this.rowIndex = rowIndex; this.session = session; } private SHPCursor(H2TableIndex tIndex, SearchRow begin, SearchRow end, Session session) { this.tIndex = tIndex; this.session = session; this.begin = begin; this.end = end; this.rowIndex = begin == null ? 0 : begin.getKey() - 1; } @Override public Row get() { return tIndex.getRow(session, rowIndex); } @Override public SearchRow getSearchRow() { Row row = session.createRow(new Value[tIndex.getTable().getColumns().length], Row.MEMORY_CALCULATE); row.setKey(rowIndex); // Add indexed columns values for(IndexColumn column : tIndex.getIndexColumns()) { if(column.column.getColumnId() >= 0) { row.setValue(column.column.getColumnId(), ValueLong.get(rowIndex)); } } return row; } @Override public boolean next() { if(rowIndex < tIndex.getRowCount(session) && (end == null || rowIndex < end.getKey())) { rowIndex ++; return true; } else { return false; } } @Override public boolean previous() { if(rowIndex > 0 && (begin == null || rowIndex >= begin.getKey())) { rowIndex --; return true; } else { return false; } } } }