/* * XXL: The eXtensible and fleXible Library for data processing * * Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department * of Mathematics and Computer Science University of Marburg Germany * * This library 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; either version 3 * of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public License along with this library; * If not, see <http://www.gnu.org/licenses/>. * * http://code.google.com/p/xxl/ */ package xxl.core.indexStructures.builder.BPlusTree; import static xxl.core.collections.containers.io.BlockFileContainer.CTR_FILE; import static xxl.core.collections.containers.io.BlockFileContainer.EXTENSIONS; import static xxl.core.collections.containers.io.BlockFileContainer.FLT_FILE; import static xxl.core.collections.containers.io.BlockFileContainer.MTD_FILE; import static xxl.core.collections.containers.io.BlockFileContainer.RBM_FILE; import static xxl.core.collections.containers.io.BlockFileContainer.UBM_FILE; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.rmi.NoSuchObjectException; import java.sql.Date; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import xxl.core.collections.containers.Container; import xxl.core.collections.containers.io.BlockFileContainer; import xxl.core.collections.containers.io.BufferedContainer; import xxl.core.collections.containers.io.ConverterContainer; import xxl.core.indexStructures.BPlusIndexedSet; import xxl.core.indexStructures.BPlusTree; import xxl.core.indexStructures.BPlusTree.IndexEntry; import xxl.core.indexStructures.BPlusTree.KeyRange; import xxl.core.indexStructures.Separator; import xxl.core.indexStructures.keyRanges.BooleanKeyRange; import xxl.core.indexStructures.keyRanges.ByteKeyRange; import xxl.core.indexStructures.keyRanges.DateKeyRange; import xxl.core.indexStructures.keyRanges.DoubleKeyRange; import xxl.core.indexStructures.keyRanges.FloatKeyRange; import xxl.core.indexStructures.keyRanges.IntegerKeyRange; import xxl.core.indexStructures.keyRanges.LongKeyRange; import xxl.core.indexStructures.keyRanges.ShortKeyRange; import xxl.core.indexStructures.keyRanges.TimeKeyRange; import xxl.core.indexStructures.keyRanges.TimestampKeyRange; import xxl.core.indexStructures.keyRanges.TupleKeyRangeFunction; import xxl.core.indexStructures.separators.BooleanSeparator; import xxl.core.indexStructures.separators.ByteSeparator; import xxl.core.indexStructures.separators.DateSeparator; import xxl.core.indexStructures.separators.DoubleSeparator; import xxl.core.indexStructures.separators.FloatSeparator; import xxl.core.indexStructures.separators.IntegerSeparator; import xxl.core.indexStructures.separators.LongSeparator; import xxl.core.indexStructures.separators.ShortSeparator; import xxl.core.indexStructures.separators.TimeSeparator; import xxl.core.indexStructures.separators.TimestampSeparator; import xxl.core.indexStructures.separators.TupleSeparator; import xxl.core.indexStructures.builder.IndexBuilder; import xxl.core.indexStructures.builder.IndexConfiguration; import xxl.core.indexStructures.builder.IndexConfiguration.Location; import xxl.core.indexStructures.builder.BPlusTree.BPlusConfiguration.Creator; import xxl.core.io.converters.meta.ExtendedResultSetMetaData; import xxl.core.io.converters.BooleanConverter; import xxl.core.io.converters.ByteConverter; import xxl.core.io.converters.DateConverter; import xxl.core.io.converters.DoubleConverter; import xxl.core.io.converters.FloatConverter; import xxl.core.io.converters.IntegerConverter; import xxl.core.io.converters.LongConverter; import xxl.core.io.converters.ShortConverter; import xxl.core.io.converters.TimeConverter; import xxl.core.io.converters.TimestampConverter; import xxl.core.io.propertyList.Property; import xxl.core.io.propertyList.PropertyList; import xxl.core.io.propertyList.json.JSONPrinter; import xxl.core.io.propertyList.json.JSONReader; import xxl.core.relational.JavaType; import xxl.core.relational.metaData.ColumnMetaInfo; import xxl.core.relational.metaData.TupleMetaData; import xxl.core.relational.tuples.ColumnComparableArrayTuple; import xxl.core.util.Pair; import xxl.core.util.ConvertUtils; import xxl.core.util.FileUtils; /** * A concrete implementation of {@link IndexBuilder} to easily create or restore an index with * {@link BPlusTree} type. The construction especially the configuration like {@link Container * Containers}, type management, storing in memory or persistent storage and so on depends on a * given configuration set which is a {@link Creator} object. <br/> * <br/> * * XXL provides a set of built-in builders, see sub classes of {@link IndexBuilder}. * * @see Creator Setting up requirements for the BPlus tree * @see IndexBuilder Base class for all index builders * @see BPlusTree The index structure itself * * @author Marcus Pinnecke (pinnecke@mathematik.uni-marburg.de) * */ public class BPlusTreeBuilder<E> extends IndexBuilder<BPlusTreeBuilder, Creator> { /* * Extends a given column with the feature of comparing to another column */ static class ColumnMetaInfoItem implements Comparable<ColumnMetaInfoItem> { /* * The columns index of this column */ Integer columnIndex; /* * The column it self */ ColumnMetaInfo content; /** * Creates a new instance of {@link ColumnMetaInfo} which is a comparable column. * * @param columnIndex The column index from a given table * @param content The column it self */ public ColumnMetaInfoItem(Integer columnIndex, ColumnMetaInfo content) { this.columnIndex = columnIndex; this.content = content; } @Override public int compareTo(ColumnMetaInfoItem o) { return columnIndex.compareTo(o.columnIndex); } } /* * The file extension for meta information. In order to store meta file of the BPlus tree and * table information the file name path is taken from BPlusTreeConfiguration. */ private static final String META_DATA_FILE_EXTENSION = "Meta.json"; public static final String PROPERTY_BLOCK_SIZE = "Block size"; public static final String PROPERTY_COLUMN_OBJECT = "Column"; public static final String PROPERTY_CONTENT_TYPE = "Content Type"; /* * The always required property names inside a PropertyList which contains information about a * BPlusTree */ public static final String PROPERTY_INDEX_TYPE = "Index Type"; public static final String PROPERTY_INDEX_TYPE_BPLUS = "BPlusTree"; public static final String PROPERTY_KEY_INDICES = "Key indices"; private static final String PROPERTY_TABLE_COLUMN_AUTOINCREMENT = "Auto Increment"; private static final String PROPERTY_TABLE_COLUMN_CASE_SENSITIVE = "Case Sensitive"; public static final String PROPERTY_TABLE_COLUMN_CATALOG = "Catalog Name"; public static final String PROPERTY_TABLE_COLUMN_CLASS = "Column Class Name"; public static final String PROPERTY_TABLE_COLUMN_CONTENT_LENGTH = "Content length"; private static final String PROPERTY_TABLE_COLUMN_CURRENCY = "Currency"; private static final String PROPERTY_TABLE_COLUMN_DEF_WRITABLE = "Def Writable"; public static final String PROPERTY_TABLE_COLUMN_DISPLAY_SIZE = "Column Display Size"; /* * If the tree manages tuple types the following information are additionally required inside a * PropertyList which contains information about a BPlusTree */ public static final String PROPERTY_TABLE_COLUMN_INDEX = "Index"; public static final String PROPERTY_TABLE_COLUMN_LABEL = "Column Label"; public static final String PROPERTY_TABLE_COLUMN_NAME = "Column Name"; private static final String PROPERTY_TABLE_COLUMN_NULLABLE = "Nullable"; public static final String PROPERTY_TABLE_COLUMN_PRECISION = "Precision"; private static final String PROPERTY_TABLE_COLUMN_READONLY = "ReadOnly"; public static final String PROPERTY_TABLE_COLUMN_SCALE = "Scale"; public static final String PROPERTY_TABLE_COLUMN_SCHEMA = "Schema Name"; private static final String PROPERTY_TABLE_COLUMN_SEARCHABLE = "Searchable"; private static final String PROPERTY_TABLE_COLUMN_SINGED = "Signed"; public static final String PROPERTY_TABLE_COLUMN_TYPE = "Column Type"; public static final String PROPERTY_TABLE_COLUMN_TYPE_NAME = "Column Type Name"; private static final String PROPERTY_TABLE_COLUMN_WRITABLE = "Writable"; public static final String PROPERTY_TABLE_COLUMNS = "Table columns"; public static final String PROPERTY_TABLE_NAME = "Table name"; /* * Unpacks the meta data of a BPlusTree from a given PropertyList instance and returns the * corresponding tree builder */ public static BPlusTreeBuilder unserialize(PropertyList serializedInstance, String storeTreeFilePath) throws NoSuchObjectException { if (serializedInstance == null) throw new NullPointerException( "Unable to unserialize BPlusTree, given serializedInstance is null"); /* * Check if property list describes a BPlus tree */ List<String> treeProperties = new ArrayList<>(); treeProperties.add(PROPERTY_INDEX_TYPE); treeProperties.add(PROPERTY_TABLE_NAME); treeProperties.add(PROPERTY_CONTENT_TYPE); treeProperties.add(PROPERTY_BLOCK_SIZE); if (!serializedInstance.containsAllProperties(treeProperties)) throw new NoSuchObjectException( "Given property list does not describe a BPlus tree. At least on property name is missing (required: " + Arrays.toString(treeProperties .toArray(new String[treeProperties.size()])) + ")"); String indexType = (String) serializedInstance.getProperty(PROPERTY_INDEX_TYPE); String contentType = (String) serializedInstance.getProperty(PROPERTY_CONTENT_TYPE); if (indexType.compareToIgnoreCase(PROPERTY_INDEX_TYPE_BPLUS) != 0) throw new NoSuchObjectException( "Given property list does not describe a BPlus tree. (type is \"" + indexType + "\")"); /* * It's a BPlus tree, now check the type if it's for primitive or tuple data. Load tree meta * data, which is stored by original BPlusTree save schema */ if (contentType.startsWith(PrimitiveType.TYPE_PREFIX)) { return unserializePrimitiveType(serializedInstance, storeTreeFilePath); } else if (contentType.startsWith(TupleType.TYPE_NAME)) { return unserializeTupleType(serializedInstance, storeTreeFilePath); } else throw new UnsupportedOperationException( "Unknown content-type for index structure (what is \"" + contentType + "\")"); } /* * Unpacks the required information for a builder from a given PropertyList and returns a tree * builder with this information. Note: this method restores trees which manages primitive data * types like Integer. */ private static BPlusTreeBuilder unserializePrimitiveType( PropertyList serializedInstance, String storeTreeFilePath) { JavaType restoreType = ConvertUtils.toJavaType(((String) serializedInstance .getProperty(PROPERTY_CONTENT_TYPE)) .substring(PrimitiveType.TYPE_PREFIX.length())); String tableName = (String) serializedInstance.getProperty(PROPERTY_TABLE_NAME); int blockSize = (int) serializedInstance.getProperty(PROPERTY_BLOCK_SIZE); Creator requirements = new Creator(new PrimitiveType(restoreType, tableName)); requirements.setBlockSize(blockSize); return (BPlusTreeBuilder) requirements.setReloadMode(storeTreeFilePath) .getBuilder(); } /* * Unpacks the required information for a builder from a given PropertyList and returns a tree * builder with this information. Note: this method restores trees which manages tuple data types. */ private static BPlusTreeBuilder unserializeTupleType( PropertyList serializedInstance, String storeTreeFilePath) throws NoSuchObjectException { /* * The stored table name */ String tableName = (String) serializedInstance.getProperty(PROPERTY_TABLE_NAME); /* * The stored block size */ int blockSize = (int) serializedInstance.getProperty(PROPERTY_BLOCK_SIZE); /* * Check if property list is well formed */ List<String> tupleTreeProperties = new ArrayList<String>(); tupleTreeProperties.add(PROPERTY_KEY_INDICES); tupleTreeProperties.add(PROPERTY_TABLE_COLUMNS); if (!serializedInstance.containsAllProperties(tupleTreeProperties)) throw new NoSuchObjectException( "Given property list does not describe a BPlus tree for tuples. At least on property name is missing (required: " + Arrays.toString(tupleTreeProperties .toArray(new String[tupleTreeProperties.size()])) + ")"); /* * Load data */ ArrayList<Object> keyIndicesObj = (ArrayList<Object>) serializedInstance .getProperty(PROPERTY_KEY_INDICES); int[] keyIndices = new int[keyIndicesObj.size()]; for (int i = 0; i < keyIndices.length; i++) keyIndices[i] = (int) keyIndicesObj.get(i); ArrayList<Object> columnsObj = (ArrayList<Object>) serializedInstance .getProperty(PROPERTY_TABLE_COLUMNS); String[] columns = new String[columnsObj.size()]; for (int i = 0; i < columns.length; i++) columns[i] = (String) columnsObj.get(i); List<String> columnProperties = new ArrayList<String>(); columnProperties.add(PROPERTY_TABLE_COLUMN_CATALOG); columnProperties.add(PROPERTY_TABLE_COLUMN_CLASS); columnProperties.add(PROPERTY_TABLE_COLUMN_CONTENT_LENGTH); columnProperties.add(PROPERTY_TABLE_COLUMN_DISPLAY_SIZE); columnProperties.add(PROPERTY_TABLE_COLUMN_INDEX); columnProperties.add(PROPERTY_TABLE_COLUMN_LABEL); columnProperties.add(PROPERTY_TABLE_COLUMN_NAME); columnProperties.add(PROPERTY_TABLE_COLUMN_PRECISION); columnProperties.add(PROPERTY_TABLE_COLUMN_SCALE); columnProperties.add(PROPERTY_TABLE_COLUMN_SCHEMA); columnProperties.add(PROPERTY_TABLE_COLUMN_TYPE); columnProperties.add(PROPERTY_TABLE_COLUMN_TYPE_NAME); columnProperties.add(PROPERTY_TABLE_COLUMN_AUTOINCREMENT); columnProperties.add(PROPERTY_TABLE_COLUMN_CASE_SENSITIVE); columnProperties.add(PROPERTY_TABLE_COLUMN_CURRENCY); columnProperties.add(PROPERTY_TABLE_COLUMN_DEF_WRITABLE); columnProperties.add(PROPERTY_TABLE_COLUMN_NULLABLE); columnProperties.add(PROPERTY_TABLE_COLUMN_READONLY); columnProperties.add(PROPERTY_TABLE_COLUMN_SEARCHABLE); columnProperties.add(PROPERTY_TABLE_COLUMN_SINGED); columnProperties.add(PROPERTY_TABLE_COLUMN_WRITABLE); List<ColumnMetaInfoItem> sortedColumnMetaInfos = new ArrayList<>(); for (String column : columns) { PropertyList columnPropertyList = serializedInstance.getPropertyList(column); if (!columnPropertyList.containsAllProperties(columnProperties)) throw new NoSuchObjectException( "Given property list does not describe a column. At least on property name is missing (required: " + Arrays.toString(columnProperties .toArray(new String[columnProperties.size()])) + ")"); int columnIndex = (int) columnPropertyList.getProperty(PROPERTY_TABLE_COLUMN_INDEX); String catalog = (String) columnPropertyList .getProperty(PROPERTY_TABLE_COLUMN_CATALOG); String clazz = (String) columnPropertyList.getProperty(PROPERTY_TABLE_COLUMN_CLASS); int contentLength = (int) columnPropertyList .getProperty(PROPERTY_TABLE_COLUMN_CONTENT_LENGTH); int displaySize = (int) columnPropertyList .getProperty(PROPERTY_TABLE_COLUMN_DISPLAY_SIZE); String columnLabel = (String) columnPropertyList.getProperty(PROPERTY_TABLE_COLUMN_LABEL); String columnName = (String) columnPropertyList.getProperty(PROPERTY_TABLE_COLUMN_NAME); int precision = (int) columnPropertyList.getProperty(PROPERTY_TABLE_COLUMN_PRECISION); int scale = (int) columnPropertyList.getProperty(PROPERTY_TABLE_COLUMN_SCALE); String schema = (String) columnPropertyList.getProperty(PROPERTY_TABLE_COLUMN_SCHEMA); int type = (int) columnPropertyList.getProperty(PROPERTY_TABLE_COLUMN_TYPE); String columnTypeName = (String) columnPropertyList .getProperty(PROPERTY_TABLE_COLUMN_TYPE_NAME); boolean autoIncrementEnabled = (Boolean) columnPropertyList .getProperty(PROPERTY_TABLE_COLUMN_AUTOINCREMENT); boolean caseSensitive = (Boolean) columnPropertyList .getProperty(PROPERTY_TABLE_COLUMN_CASE_SENSITIVE); boolean currency = (Boolean) columnPropertyList .getProperty(PROPERTY_TABLE_COLUMN_CURRENCY); boolean definitelyWritable = (Boolean) columnPropertyList .getProperty(PROPERTY_TABLE_COLUMN_DEF_WRITABLE); int nullable = (int) columnPropertyList.getProperty(PROPERTY_TABLE_COLUMN_NULLABLE); boolean readOnly = (Boolean) columnPropertyList .getProperty(PROPERTY_TABLE_COLUMN_READONLY); boolean searchable = (Boolean) columnPropertyList .getProperty(PROPERTY_TABLE_COLUMN_SEARCHABLE); boolean singed = (Boolean) columnPropertyList .getProperty(PROPERTY_TABLE_COLUMN_SINGED); boolean writable = (Boolean) columnPropertyList .getProperty(PROPERTY_TABLE_COLUMN_WRITABLE); ColumnMetaInfo columnMetaInfo = null; if (ConvertUtils.toJavaType(ConvertUtils.toRelationalType(type)) == JavaType.STRING) columnMetaInfo = new ColumnMetaInfo(ConvertUtils.toRelationalType(type), contentLength, columnName); else columnMetaInfo = new ColumnMetaInfo(ConvertUtils.toRelationalType(type), columnName); columnMetaInfo.setAutoIncrementEnabled(autoIncrementEnabled); columnMetaInfo.setCaseSensitive(caseSensitive); columnMetaInfo.setCatalogName(catalog); columnMetaInfo.setColumnClassName(clazz); columnMetaInfo.setColumnDisplaySize(displaySize); columnMetaInfo.setColumnLabel(columnLabel); columnMetaInfo.setColumnTypeName(columnTypeName); columnMetaInfo.setCurrency(currency); columnMetaInfo.setDefinitelyWritable(definitelyWritable); columnMetaInfo.setNullable(nullable); columnMetaInfo.setPrecision(precision); columnMetaInfo.setReadOnly(readOnly); columnMetaInfo.setScale(scale); columnMetaInfo.setSchemaName(schema); columnMetaInfo.setSearchable(searchable); columnMetaInfo.setSinged(singed); columnMetaInfo.setWritable(writable); sortedColumnMetaInfos.add(new ColumnMetaInfoItem(columnIndex, columnMetaInfo)); } /* * Build columns list in the column order given by property list */ List<ColumnMetaInfo> columnMeta = new ArrayList<>(); Collections.sort(sortedColumnMetaInfos); for (ColumnMetaInfoItem item : sortedColumnMetaInfos) columnMeta.add(item.content); try { TupleMetaData tupleMetaData = new TupleMetaData(tableName, columnMeta.toArray(new ColumnMetaInfo[columnMeta.size()])); Creator requirements = new Creator(new TupleType(tupleMetaData)); requirements.setBlockSize(blockSize); requirements.setCompoundKey(keyIndices); return (BPlusTreeBuilder) requirements.setReloadMode(storeTreeFilePath) .getBuilder(); } catch (Exception e) { e.printStackTrace(); } return null; } /* * Enables or disables the reloading mode. By default a new tree should be created and not loaded. */ protected boolean mReload = false; /* * Flag to indicate if this builder is in reloading mode or not. By default it is not. */ private boolean mRestoredInstance = false; /** * Setup the builder with the given configuration. * * @param configuration Your BPlus tree requirements */ public BPlusTreeBuilder(BPlusConfiguration.Creator configuration) { super(configuration); } /** * Constructs, initialize and returns a BPlusTree which fits your requirements given by * <code>BPlusTreeConfiguration</code> in the constructor of this object. */ @Override public BPlusIndexedSet<E> create() { BPlusConfiguration configuration = (BPlusConfiguration) mIndexConfiguration; BPlusTree retval = new BPlusTree(configuration.getBlockSize()); Container bufferedContainer = null; Container fileContainer = null; if (mReload) { String tableDomain = configuration.getFileSystemFilePath() + File.separatorChar + configuration.getManagedType().getTableName(); File checkCtr = new File(tableDomain + EXTENSIONS[CTR_FILE]); File checkFlt = new File(tableDomain + EXTENSIONS[FLT_FILE]); File checkMtd = new File(tableDomain + EXTENSIONS[MTD_FILE]); File checkRbm = new File(tableDomain + EXTENSIONS[RBM_FILE]); File checkUbm = new File(tableDomain + EXTENSIONS[UBM_FILE]); File checkMeta = new File(tableDomain + BPlusIndexedSet.META_FILE_EXTENSION); File checkJson = new File(tableDomain + "Meta." + JSONReader.FILE_EXTENSION); List<String> missingFileList = new ArrayList<>(); if (!checkCtr.exists() || checkCtr.length() == 0) missingFileList.add(checkCtr.getName()); if (!checkFlt.exists()) missingFileList.add(checkFlt.getName()); if (!checkMtd.exists() || checkMtd.length() == 0) missingFileList.add(checkMtd.getName()); if (!checkRbm.exists() || checkRbm.length() == 0) missingFileList.add(checkUbm.getName()); if (!checkUbm.exists() || checkUbm.length() == 0) missingFileList.add(checkUbm.getName()); if (!checkMeta.exists() || checkMeta.length() == 0) missingFileList.add(checkMeta.getName()); if (!checkJson.exists() || checkJson.length() == 0) missingFileList.add(checkJson.getName()); if (!missingFileList.isEmpty()) throw new IllegalAccessError( "At least one required file is missing or empty in \"" + FileUtils.getFilePath(tableDomain) + "\". Check \n" + Arrays.toString(missingFileList .toArray(new String[missingFileList.size()]))); fileContainer = new BlockFileContainer(tableDomain); } else { if (configuration.storeAtFileSystem()) fileContainer = configuration.getFileContainer(); } if (configuration.getLocation().equals(Location.LOCATION_FILESYSTEM)) { Container converterContainer = new ConverterContainer(fileContainer, retval.nodeConverter()); bufferedContainer = new BufferedContainer(converterContainer, configuration.getBuffer()); configuration.setBufferedContainer((BufferedContainer) bufferedContainer); } else bufferedContainer = configuration.getBufferedContainer(); configuration.setBufferedContainer((BufferedContainer) bufferedContainer); try { if (configuration.storeAtFileSystem() && (configuration instanceof BPlusConfiguration.Creator)) storeMetaData((Creator) configuration); } catch (Exception e) { throw new RuntimeException(Arrays.toString(e.getStackTrace()) + "\n" + e.getMessage()); } if (mReload) { try { Pair<IndexEntry, KeyRange> restoredTreeMeta = readTree(retval, configuration.getFileSystemFilePath() + File.separatorChar + configuration.getManagedType().getTableName() + BPlusIndexedSet.META_FILE_EXTENSION, configuration); retval.initialize(restoredTreeMeta.getElement1(), restoredTreeMeta .getElement2(), configuration.getKeyFunctionFactory() .getKeyFunction(), bufferedContainer, configuration .getKeyFunctionFactory().getKeyConverter(), configuration .getDataConverter(), configuration.getKeyFunctionFactory() .getKeyValueSeparatorFunction(), configuration .getKeyFunctionFactory().getKeyRangeFunction(1)); } catch (Exception e) { e.printStackTrace(); throw new IllegalArgumentException(); } } else { retval.initialize(null, null, configuration.getKeyFunctionFactory() .getKeyFunction(), bufferedContainer, configuration .getKeyFunctionFactory().getKeyConverter(), configuration .getDataConverter(), configuration.getKeyFunctionFactory() .getKeyValueSeparatorFunction(), configuration .getKeyFunctionFactory().getKeyRangeFunction(1)); } return new BPlusIndexedSet(retval, this); } /* * This package wide visible method enables the reloading mode and returns the current instance. */ BPlusTreeBuilder enableLoadingMode() { mRestoredInstance = true; return this; } /** * Enables or disables the reloading mode. * * @return BPlusTreeBuilder */ public BPlusTreeBuilder enableReload() { mReload = true; return this; } /* * Reloads the required IndexEntry and KeyRange of the previously stored BPlusTree. Please note * reloading for primitive types is done with XXL built-in converter (which loads the information * from a binary file (reading order matters!)) but tuple types is done this * JSONReader/PropertyList (which loads the information from a plain text file (reading order does * not matter)). * * In general the reading requires * a source where the meta data is taken from (String in, a * folder) * the tree's level information * the tree's root key id * the tree's root separator * * the tree's key range * an instance of BPlusTree which is initialized with this information * (BPlusTree tree) * a configuration which specifies which files from "in" should be loaded an * which kind of data should be managed * * the return value is a pair of the tree's IndexEntry (built with level, key id and root * separator) and the key range. */ private Pair<IndexEntry, KeyRange> readTree(BPlusTree tree, String in, BPlusConfiguration configuration) throws IOException { DataInputStream input = new DataInputStream(new FileInputStream(new File(in))); int level = IntegerConverter.DEFAULT_INSTANCE.readInt(input); Long id = -1l; Separator rootSeparator = null; IndexEntry rootEntry = null; KeyRange keyRange = null; switch (configuration.getManagedType().getContentClass()) { case CONTENT_CLASS_PRIMITIVE: { JavaType subclass = configuration.getManagedType().getContentClassSubType(); switch (subclass) { case BOOLEAN: { Boolean rootKey = BooleanConverter.DEFAULT_INSTANCE.read(input); id = LongConverter.DEFAULT_INSTANCE.readLong(input); Boolean minKey = BooleanConverter.DEFAULT_INSTANCE.read(input); Boolean maxKey = BooleanConverter.DEFAULT_INSTANCE.read(input); rootSeparator = new BooleanSeparator(rootKey); keyRange = new BooleanKeyRange(minKey, maxKey); } break; case BYTE: { Byte rootKey = ByteConverter.DEFAULT_INSTANCE.read(input); id = LongConverter.DEFAULT_INSTANCE.readLong(input); Byte minKey = ByteConverter.DEFAULT_INSTANCE.read(input); Byte maxKey = ByteConverter.DEFAULT_INSTANCE.read(input); rootSeparator = new ByteSeparator(rootKey); keyRange = new ByteKeyRange(minKey, maxKey); } break; case DATE: { Date rootKey = DateConverter.DEFAULT_INSTANCE.read(input); id = LongConverter.DEFAULT_INSTANCE.readLong(input); Date minKey = DateConverter.DEFAULT_INSTANCE.read(input); Date maxKey = DateConverter.DEFAULT_INSTANCE.read(input); rootSeparator = new DateSeparator(rootKey.getTime()); keyRange = new DateKeyRange(minKey.getTime(), maxKey.getTime()); } break; case DOUBLE: { Double rootKey = DoubleConverter.DEFAULT_INSTANCE.readDouble(input); id = LongConverter.DEFAULT_INSTANCE.readLong(input); Double minKey = DoubleConverter.DEFAULT_INSTANCE.readDouble(input); Double maxKey = DoubleConverter.DEFAULT_INSTANCE.readDouble(input); rootSeparator = new DoubleSeparator(rootKey); keyRange = new DoubleKeyRange(minKey, maxKey); } break; case FLOAT: { Float rootKey = FloatConverter.DEFAULT_INSTANCE.read(input); id = LongConverter.DEFAULT_INSTANCE.readLong(input); Float minKey = FloatConverter.DEFAULT_INSTANCE.read(input); Float maxKey = FloatConverter.DEFAULT_INSTANCE.read(input); rootSeparator = new FloatSeparator(rootKey); keyRange = new FloatKeyRange(minKey, maxKey); } break; case INT: { Integer rootKey = IntegerConverter.DEFAULT_INSTANCE.readInt(input); id = LongConverter.DEFAULT_INSTANCE.readLong(input); Integer minKey = IntegerConverter.DEFAULT_INSTANCE.readInt(input); Integer maxKey = IntegerConverter.DEFAULT_INSTANCE.readInt(input); rootSeparator = new IntegerSeparator(rootKey); keyRange = new IntegerKeyRange(minKey, maxKey); } break; case LONG: { Long rootKey = LongConverter.DEFAULT_INSTANCE.readLong(input); id = LongConverter.DEFAULT_INSTANCE.readLong(input); Long minKey = LongConverter.DEFAULT_INSTANCE.readLong(input); Long maxKey = LongConverter.DEFAULT_INSTANCE.readLong(input); rootSeparator = new LongSeparator(rootKey); keyRange = new LongKeyRange(minKey, maxKey); } break; case SHORT: { Short rootKey = ShortConverter.DEFAULT_INSTANCE.read(input); id = LongConverter.DEFAULT_INSTANCE.readLong(input); Short minKey = ShortConverter.DEFAULT_INSTANCE.read(input); Short maxKey = ShortConverter.DEFAULT_INSTANCE.read(input); rootSeparator = new ShortSeparator(rootKey); keyRange = new ShortKeyRange(minKey, maxKey); } break; case TIME: { Time rootKey = TimeConverter.DEFAULT_INSTANCE.read(input); id = LongConverter.DEFAULT_INSTANCE.readLong(input); Time minKey = TimeConverter.DEFAULT_INSTANCE.read(input); Time maxKey = TimeConverter.DEFAULT_INSTANCE.read(input); rootSeparator = new TimeSeparator(rootKey); keyRange = new TimeKeyRange(minKey, maxKey); } break; case TIMESTAMP: { Timestamp rootKey = TimestampConverter.DEFAULT_INSTANCE.read(input); id = LongConverter.DEFAULT_INSTANCE.readLong(input); Timestamp minKey = TimestampConverter.DEFAULT_INSTANCE.read(input); Timestamp maxKey = TimestampConverter.DEFAULT_INSTANCE.read(input); rootSeparator = new TimestampSeparator(rootKey); keyRange = new TimestampKeyRange(minKey, maxKey); } break; default: throw new UnsupportedOperationException( "Not implemented yet for \"" + subclass + "\""); } } break; case CONTENT_CLASS_COMPLEX: { try { BPlusConfiguration config = (BPlusConfiguration) mIndexConfiguration; ResultSetMetaData metaData = config.getManagedType().getMetaData(); String filename = config.getFileSystemFilePath() + "/" + metaData.getTableName(0) + BPlusIndexedSet.BPLUS_TUPLE_MET_EXTENSION; JSONReader reader = new JSONReader(); PropertyList treeInformation = reader .read(new BufferedInputStream(new FileInputStream(filename))); // Check if format is valid List<String> keys = new ArrayList<>(); keys.add("RootKey"); keys.add("RootID"); keys.add("MinKey"); keys.add("MaxKey"); if (!treeInformation.containsAllProperties(keys)) throw new IllegalArgumentException("File \"" + filename + "\" is not a tree meta file or it is damaged."); ArrayList<Object> rootKey = (ArrayList) treeInformation.getProperty("RootKey"); id = Long.valueOf(String.valueOf(treeInformation.getProperty("RootID"))); ArrayList<Object> minKey = (ArrayList) treeInformation.getProperty("MinKey"); ArrayList<Object> maxKey = (ArrayList) treeInformation.getProperty("MaxKey"); rootSeparator = new TupleSeparator(new ColumnComparableArrayTuple( rootKey.toArray(new Object[rootKey.size()]))); keyRange = new TupleKeyRangeFunction(new ColumnComparableArrayTuple( minKey.toArray(new Object[minKey.size()])), new ColumnComparableArrayTuple(maxKey .toArray(new Object[maxKey.size()]))); } catch (SQLException _) {}; } break; default: throw new UnsupportedOperationException( "Unknown Content class: Not supported yet"); } rootEntry = ((IndexEntry) tree.createIndexEntry(level).initialize(id)) .initialize(rootSeparator); return new Pair<>(rootEntry, keyRange); } /* * Packs the meta data of a BPlusTree (e.g. table name, content type etc.) into a PropertyList * object. * * @see xxl.core.io.propertyList.IPropertyListSerializable#serialize(java.lang.Object) */ @Override public PropertyList serialize(Creator config) throws Exception { PropertyList treeInfo = new PropertyList(); treeInfo.add(new Property(PROPERTY_INDEX_TYPE, PROPERTY_INDEX_TYPE_BPLUS)); treeInfo.add(new Property(PROPERTY_TABLE_NAME, config.getTableName())); treeInfo.add(new Property(PROPERTY_CONTENT_TYPE, config.getContentType())); treeInfo.add(new Property(PROPERTY_BLOCK_SIZE, config.getBlockSize())); /* * Write additional meta data for columns etc. if the the BPlus tree stores tuples */ if (config.getManagedType() instanceof TupleType) { int[] ki = config.getCompoundKeyIndices(); Object[] keyIndices = new Object[ki.length]; for (int i = 0; i < ki.length; i++) keyIndices[i] = ki[i]; ExtendedResultSetMetaData tableMetaData = config.getMetaData(); treeInfo.add(new Property(PROPERTY_KEY_INDICES, keyIndices)); int columnCount = tableMetaData.getColumnCount(); String[] columnNameArray = new String[columnCount]; for (int i = 0; i < columnCount; i++) columnNameArray[i] = PROPERTY_COLUMN_OBJECT + (i + 1); treeInfo.add(new Property(PROPERTY_TABLE_COLUMNS, columnNameArray)); // Object[] columnInformation = new Object[columnCount]; for (int i = 1; i <= columnCount; i++) { PropertyList columnInfo = new PropertyList(); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_INDEX, i)); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_CATALOG, tableMetaData.getCatalogName(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_CLASS, tableMetaData .getColumnClassName(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_DISPLAY_SIZE, tableMetaData.getColumnDisplaySize(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_LABEL, tableMetaData .getColumnLabel(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_NAME, tableMetaData .getColumnName(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_TYPE, tableMetaData .getColumnType(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_TYPE_NAME, tableMetaData.getColumnTypeName(i))); int columnType = tableMetaData.getColumnType(i); JavaType javaType = ConvertUtils.toJavaType(ConvertUtils.toRelationalType(columnType)); if (javaType.equals(JavaType.STRING)) columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_CONTENT_LENGTH, tableMetaData.getContentLength(i))); else columnInfo .add(new Property(PROPERTY_TABLE_COLUMN_CONTENT_LENGTH, -1)); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_PRECISION, tableMetaData.getPrecision(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_SCALE, tableMetaData .getScale(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_SCHEMA, tableMetaData .getSchemaName(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_AUTOINCREMENT, tableMetaData.isAutoIncrement(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_CASE_SENSITIVE, tableMetaData.isCaseSensitive(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_CURRENCY, tableMetaData.isCurrency(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_DEF_WRITABLE, tableMetaData.isDefinitelyWritable(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_NULLABLE, tableMetaData.isNullable(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_READONLY, tableMetaData.isReadOnly(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_SEARCHABLE, tableMetaData.isSearchable(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_SINGED, tableMetaData .isSigned(i))); columnInfo.add(new Property(PROPERTY_TABLE_COLUMN_WRITABLE, tableMetaData.isWritable(i))); treeInfo.add(new Property(PROPERTY_COLUMN_OBJECT + i, columnInfo)); } } return treeInfo; } /** * Store BPlus meta data * * @param config Source configuration which contains the information * @throws Exception */ private void storeMetaData(Creator config) throws Exception { File metaDataFile = new File(config.getFileSystemFilePath() + META_DATA_FILE_EXTENSION); FileOutputStream metaDataOutput = new FileOutputStream(metaDataFile); PropertyList treeInfo = serialize(config); JSONPrinter treeInfoPrinter = new JSONPrinter(treeInfo); treeInfoPrinter.print(metaDataOutput); } /** * Prints the underlying index configuration */ public String toString() { return ((IndexConfiguration) mIndexConfiguration).toString(); } }