/** * Copyright (C) 2009-2015 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.qp.storeadapter.indexrow; import static java.lang.Math.min; import com.foundationdb.ais.model.GroupIndex; import com.foundationdb.ais.model.TableIndex; import com.foundationdb.ais.model.Index; import com.foundationdb.ais.model.IndexToHKey; import com.foundationdb.ais.model.Table; import com.foundationdb.qp.row.HKey; import com.foundationdb.qp.row.IndexRow; import com.foundationdb.qp.row.ValuesHKey; import com.foundationdb.qp.rowtype.IndexRowType; import com.foundationdb.qp.storeadapter.indexcursor.SortKeyAdapter; import com.foundationdb.qp.storeadapter.indexcursor.SortKeyTarget; import com.foundationdb.qp.storeadapter.indexcursor.ValueSortKeyAdapter; import com.foundationdb.qp.util.PersistitKey; import com.foundationdb.server.PersistitKeyValueSource; import com.foundationdb.server.service.tree.KeyCreator; import com.foundationdb.server.types.TInstance; import com.foundationdb.server.types.texpressions.TPreparedExpression; import com.foundationdb.server.types.value.ValueSource; import com.foundationdb.util.ArgumentValidation; import com.persistit.Key; import com.persistit.Value; public class MemoryIndexRow extends IndexRow { private final SortKeyAdapter<ValueSource, TPreparedExpression> SORT_KEY_ADAPTER = ValueSortKeyAdapter.INSTANCE; private Index index; private Key iKey; private Value iValue; private final IndexRowType indexRowType; private long tableBitmap; private SortKeyTarget pKeyTarget; private int pKeyFields; private final TInstance[] types; private final KeyCreator keyCreator; private HKey leafTableHKey; private int zPosition; public MemoryIndexRow(KeyCreator keyCreator) { ArgumentValidation.notNull("keyCreator", keyCreator); this.keyCreator = keyCreator; this.indexRowType = null; this.types = null; } public MemoryIndexRow(KeyCreator adapter, IndexRowType indexRowType) { ArgumentValidation.notNull("keyCreator", adapter); this.keyCreator = adapter; this.iKey = adapter.createKey(); this.indexRowType = indexRowType; this.index = indexRowType.index(); resetForWrite(index, iKey); this.types = index.types(); } // // IndexRow // @Override public IndexRowType rowType() { return indexRowType; } @Override protected ValueSource uncheckedValue(int i) { PersistitKeyValueSource source = new PersistitKeyValueSource(types[i]); source.attach(iKey, i, types[i]); return source; } @Override public HKey hKey() { assert this.leafTableHKey != null; return this.leafTableHKey; } @Override public HKey ancestorHKey(Table table) { HKey ancestorHKey; IndexToHKey indexToHKey; if(index.isGroupIndex()) { ancestorHKey = keyCreator.newHKey(table.hKey()); indexToHKey = ((GroupIndex)index).indexToHKey(table.getDepth()); } else { ancestorHKey = keyCreator.newHKey(index.leafMostTable().hKey()); indexToHKey = ((TableIndex)index).indexToHKey(); } //Short circuit if the key is empty. if(this.keyEmpty()) { this.leafTableHKey = ancestorHKey; return ancestorHKey; } int segment = 0; int columns = 0; ValuesHKey ancestor = (ValuesHKey)ancestorHKey; for(int i = 0; i < indexToHKey.getLength(); i++) { if(indexToHKey.isOrdinal(i)) { // Do nothing, the ValuesHKey should have the correct ordinals // built from the metadata. assert indexToHKey.getOrdinal(i) == ancestor.ordinals()[segment]; segment++; } else { int indexField = indexToHKey.getIndexRowPosition(i); if(index.isSpatial() && indexField > index.firstSpatialArgument()) { // A spatial index has a single key column (the z-value), representing the declared spatial key columns. indexField -= index.spatialColumns() - 1; } ancestor.copyValueTo(this.value(indexField), columns++); } } // Copy the leaf table HKey for later use. // Return the correct (shortened) HKey for others to use. if(index.isTableIndex() && index.leafMostTable() != table) { this.leafTableHKey = ancestorHKey; ancestorHKey = keyCreator.newHKey(table.hKey()); this.leafTableHKey.copyTo(ancestorHKey); ancestorHKey.useSegments(table.getDepth() + 1); } return ancestorHKey; } @SuppressWarnings("unchecked") @Override public <S> void append(S source, TInstance type) { pKeyTarget.append(source, type); } @Override public void append(EdgeValue value) { if(value == EdgeValue.BEFORE) { iKey.append(Key.BEFORE); } else if(value == EdgeValue.AFTER) { iKey.append(Key.AFTER); } else { throw new UnsupportedOperationException(value.toString()); } } @Override public void resetForRead(Index index, Key key, Value value) { this.index = index; this.iKey = key; this.iValue = value; this.pKeyTarget = null; this.pKeyFields = index.getAllColumns().size(); if(value != null) { value.getByteArray(iValue.getEncodedBytes(), 0, 0, value.getArrayLength()); iValue.setEncodedSize(value.getArrayLength()); } zPosition = index.isSpatial() ? index.firstSpatialArgument() : -1; } @Override public void resetForWrite(Index index, Key key) { this.index = index; this.iKey = key; this.iValue = null; this.pKeyFields = index.getAllColumns().size(); if(this.pKeyTarget == null) { this.pKeyTarget = SORT_KEY_ADAPTER.createTarget(index.getIndexName()); } this.pKeyTarget.attach(key); zPosition = index.isSpatial() ? index.firstSpatialArgument() : -1; } @Override public int compareTo(IndexRow thatKey, int startBoundColumns) { return compareTo(thatKey, startBoundColumns, null); } @Override public int compareTo(IndexRow thatKey, int fieldCount, boolean[] ascending) { MemoryIndexRow that = (MemoryIndexRow)thatKey; if(fieldCount <= 0) { return 0; } int c; byte[] thisBytes = this.iKey.getEncodedBytes(); byte[] thatBytes = that.iKey.getEncodedBytes(); int b = 0; // byte position int f = 0; // field position int thisEnd = this.iKey.getEncodedSize(); int thatEnd = that.iKey.getEncodedSize(); int end = min(thisEnd, thatEnd); while(b < end && f < pKeyFields) { int thisByte = thisBytes[b] & 0xff; int thatByte = thatBytes[b] & 0xff; c = thisByte - thatByte; if(c != 0) { return isSet(ascending, f, c, -c); } else { b++; if(thisByte == 0) { if(++f == fieldCount) { return 0; } } } } if(thisEnd > thatEnd) { return isSet(ascending, f, 1, -1); } if(thatEnd > thisEnd) { return isSet(ascending, f, -1, 1); } // Compare pValues, if there are any thisBytes = this.iValue == null ? null : this.iValue.getEncodedBytes(); thatBytes = that.iValue == null ? null : that.iValue.getEncodedBytes(); if(thisBytes == null && thatBytes == null) { return 0; } else if(thisBytes == null) { return isSet(ascending, f, -1, 1); } else if(thatBytes == null) { return isSet(ascending, f, 1, -1); } b = 0; thisEnd = this.iValue.getEncodedSize(); thatEnd = that.iValue.getEncodedSize(); end = min(thisEnd, thatEnd); while(b < end) { int thisByte = thisBytes[b] & 0xff; int thatByte = thatBytes[b] & 0xff; c = thisByte - thatByte; if(c != 0) { return isSet(ascending, f, c, -c); } else { b++; if(thisByte == 0) { if(++f == fieldCount) { return 0; } } } } if(thisEnd > thatEnd) { return isSet(ascending, f, 1, -1); } if(thatEnd > thisEnd) { return isSet(ascending, f, -1, 1); } return 0; } @Override public void reset() { iKey.clear(); if(iValue != null) { iValue.clear(); } } @Override public void copyPersistitKeyTo(Key key) { iKey.copyTo(key); } @Override public void appendFieldTo(int position, Key target) { PersistitKey.appendFieldFromKey(target, iKey, position, index.getIndexName()); } @Override public void copyFrom(Key key, Value value) { key.copyTo(iKey); if(value != null && value.isDefined() && !value.isNull()) { tableBitmap = value.getLong(); } leafTableHKey = ancestorHKey(index.leafMostTable()); } @Override public boolean keyEmpty() { return iKey.getEncodedSize() == 0; } @Override public void tableBitmap(long bitmap) { tableBitmap = bitmap; } @Override public long tableBitmap() { return tableBitmap; } @Override protected int zPosition() { return zPosition; } // // Static // private static int isSet(boolean[] values, int idx, int tVal, int fVal) { return ((values == null) || values[idx]) ? tVal : fVal; } }