/** * 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.DatabaseEventListener; import org.h2.api.ErrorCode; import org.h2.command.ddl.CreateTableData; import org.h2.engine.Session; import org.h2.engine.SysProperties; import org.h2.index.Cursor; import org.h2.index.Index; import org.h2.index.IndexType; import org.h2.index.SpatialTreeIndex; import org.h2.message.DbException; import org.h2.result.Row; import org.h2.table.Column; import org.h2.table.IndexColumn; import org.h2.table.TableBase; import org.h2.util.MathUtils; import org.h2.util.New; import org.h2.value.Value; import org.h2gis.functions.io.FileDriver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import org.h2.table.TableType; /** * A table linked with a {@link org.h2gis.drivers.FileDriver} * @author Nicolas Fortin */ public class H2Table extends TableBase { private FileDriver driver; private static final Logger LOG = LoggerFactory.getLogger(H2Table.class); private final ArrayList<Index> indexes = New.arrayList(); private Column rowIdColumn; public H2Table(FileDriver driver, CreateTableData data) throws IOException { super(data); indexes.add(new H2TableIndex(driver,this,this.getId(), data.columns.get(0), data.schema.getUniqueIndexName(data.session, this,data.tableName + "." + data.columns.get(0).getName() + "_INDEX_"))); this.driver = driver; } /** * Create row index * @param session database session */ public void init(Session session) { indexes.add(0, new H2TableIndex(driver,this,this.getId())); } @Override public boolean lock(Session session, boolean exclusive, boolean force) { return false; } @Override public void close(Session session) { for (Index index : indexes) { index.close(session); } try { driver.close(); } catch (IOException ex) { LOG.error("Error while closing the SHP driver", ex); } } @Override public void unlock(Session s) { //To change body of implemented methods use File | Settings | File Templates. } @Override public Row getRow(Session session, long key) { return indexes.get(0).getRow(session, key); } @Override public Index addIndex(Session session, String indexName, int indexId, IndexColumn[] cols, IndexType indexType, boolean create, String indexComment) { boolean isSessionTemporary = isTemporary() && !isGlobalTemporary(); if (!isSessionTemporary) { database.lockMeta(session); } Index index; if (isPersistIndexes() && indexType.isPersistent()) { if (indexType.isSpatial()) { index = new SpatialTreeIndex(this, indexId, indexName, cols, indexType, true, create, session); } else { throw DbException.getUnsupportedException("VIEW"); } } else { if (indexType.isSpatial()) { index = new SpatialTreeIndex(this, indexId, indexName, cols, indexType, false, true, session); } else { throw DbException.getUnsupportedException("VIEW"); } } if (index.needRebuild() && getRowCount(session) > 0) { try { Index scan = getScanIndex(session); long remaining = scan.getRowCount(session); long total = remaining; Cursor cursor = scan.find(session, null, null); long i = 0; int bufferSize = (int) Math.min(getRowCount(session), database.getMaxMemoryRows()); ArrayList<Row> buffer = New.arrayList(bufferSize); String n = getName() + ":" + index.getName(); int t = MathUtils.convertLongToInt(total); while (cursor.next()) { database.setProgress(DatabaseEventListener.STATE_CREATE_INDEX, n, MathUtils.convertLongToInt(i++), t); Row row = cursor.get(); buffer.add(row); if (buffer.size() >= bufferSize) { addRowsToIndex(session, buffer, index); } remaining--; } addRowsToIndex(session, buffer, index); if (SysProperties.CHECK && remaining != 0) { throw DbException.throwInternalError("rowcount remaining=" + remaining + " " + getName()); } } catch (DbException e) { getSchema().freeUniqueName(indexName); try { index.remove(session); } catch (DbException e2) { // this could happen, for example on failure in the storage // but if that is not the case it means // there is something wrong with the database trace.error(e2, "could not remove index"); throw e2; } throw e; } } index.setTemporary(isTemporary()); if (index.getCreateSQL() != null) { index.setComment(indexComment); if (isSessionTemporary) { session.addLocalTempTableIndex(index); } else { database.addSchemaObject(session, index); } } indexes.add(index); setModified(); return index; } private static void addRowsToIndex(Session session, ArrayList<Row> list, Index index) { final Index idx = index; Collections.sort(list, new Comparator<Row>() { @Override public int compare(Row r1, Row r2) { return idx.compareRows(r1, r2); } }); for (Row row : list) { index.add(session, row); } list.clear(); } @Override public void removeRow(Session session, Row row) { throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1,"removeRow in Shape files"); } @Override public void truncate(Session session) { for(Index index : indexes) { index.truncate(session); } } @Override public void addRow(Session session, Row row) { throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1,"addRow in Shape files"); } @Override public void checkSupportAlter() { throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1,"addRow in Shape files"); } @Override public TableType getTableType() { return TableType.EXTERNAL_TABLE_ENGINE; } @Override public Index getScanIndex(Session session) { // Look for scan index for(Index index : indexes) { if(index.getIndexType().isScan()) { return index; } } return null; } @Override public Index getUniqueIndex() { for (Index idx : indexes) { if (idx.getIndexType().isUnique()) { return idx; } } return null; } @Override public ArrayList<Index> getIndexes() { return indexes; } @Override public boolean isLockedExclusively() { return false; } @Override public long getMaxDataModificationId() { return 0; } @Override public boolean isDeterministic() { return true; } @Override public boolean canGetRowCount() { return true; } @Override public boolean canDrop() { return true; } @Override public long getRowCount(Session session) { return driver.getRowCount(); } @Override public long getRowCountApproximation() { return driver.getRowCount(); } @Override public long getDiskSpaceUsed() { return 0; } @Override public void checkRename() { //Nothing to check } @Override public Column getRowIdColumn() { if (rowIdColumn == null) { rowIdColumn = new Column(Column.ROWID, Value.LONG); rowIdColumn.setTable(this, -1); } return rowIdColumn; } }