public KVPair writeDirectIndex(LocatedRow locatedRow) throws IOException, StandardException { assert locatedRow != null : "locatedRow passed in is null"; ExecRow execRow = locatedRow.getRow(); getSerializers(execRow); EntryAccumulator keyAccumulator = getKeyAccumulator(); keyAccumulator.reset(); boolean hasNullKeyFields = false; for (int i = 0; i < execRow.nColumns(); i++) { if (execRow.getColumn(i + 1) == null || execRow.getColumn(i + 1).isNull()) { hasNullKeyFields = true; accumulateNull(keyAccumulator, i, indexFormatIds[i]); } else { byte[] data = serializers[i].encodeDirect(execRow.getColumn(i + 1), false); accumulate( keyAccumulator, i, indexFormatIds[i], index.getDescColumns(i), data, 0, data.length); } } // add the row key to the end of the index key byte[] srcRowKey = Encoding.encodeBytesUnsorted(locatedRow.getRowLocation().getBytes()); EntryEncoder rowEncoder = getRowEncoder(); MultiFieldEncoder entryEncoder = rowEncoder.getEntryEncoder(); entryEncoder.reset(); entryEncoder.setRawBytes(srcRowKey); byte[] indexValue = rowEncoder.encode(); byte[] indexRowKey; if (index.getUnique()) { boolean nonUnique = index.getUniqueWithDuplicateNulls() && (hasNullKeyFields || !keyAccumulator.isFinished()); indexRowKey = getIndexRowKey(srcRowKey, nonUnique); } else indexRowKey = getIndexRowKey(srcRowKey, true); return new KVPair(indexRowKey, indexValue, KVPair.Type.INSERT); }
private EntryEncoder getRowEncoder() { if (indexValueEncoder == null) { BitSet nonNullFields = new BitSet(); int highestSetPosition = 0; for (int keyColumn : mainColToIndexPosMap) { if (keyColumn > highestSetPosition) highestSetPosition = keyColumn; } nonNullFields.set(highestSetPosition + 1); indexValueEncoder = EntryEncoder.create( SpliceKryoRegistry.getInstance(), 1, nonNullFields, new BitSet(), new BitSet(), new BitSet()); } return indexValueEncoder; }
/** * Translate the given base table record mutation into its associated, referencing index record. * <br> * Encapsulates the logic required to create an index record for a given base table record with * all the required discriminating and encoding rules (column is part of a PK, value is null, * etc). * * @param mutation KVPair containing the rowKey of the base table record for which we want to * translate to the associated index. This mutation should already have its requred {@link * KVPair.Type Type} set. * @return A KVPair representing the index record of the given base table mutation. This KVPair is * suitable for performing the required modification of the index record associated with this * mutation. * @throws IOException for encoding/decoding problems. */ public KVPair translate(KVPair mutation) throws IOException { if (mutation == null) { return null; } EntryAccumulator keyAccumulator = getKeyAccumulator(); keyAccumulator.reset(); boolean hasNullKeyFields = false; /* * Handle index columns from the source table's primary key. */ if (table.getColumnOrderingCount() > 0) { // we have key columns to check MultiFieldDecoder keyDecoder = getSrcKeyDecoder(); keyDecoder.set(mutation.getRowKey()); for (int i = 0; i < table.getColumnOrderingCount(); i++) { int sourceKeyColumnPos = table.getColumnOrdering(i); int indexKeyPos = sourceKeyColumnPos < mainColToIndexPosMap.length ? mainColToIndexPosMap[sourceKeyColumnPos] : -1; int offset = keyDecoder.offset(); boolean isNull = skip(keyDecoder, table.getFormatIds(sourceKeyColumnPos)); if (!indexedCols.get(sourceKeyColumnPos)) continue; if (indexKeyPos >= 0) { /* * since primary keys have an implicit NOT NULL constraint here, we don't need to check for it, * and isNull==true would represent a programmer error, rather than an actual state the * system can be in. */ assert !isNull : "Programmer error: Cannot update a primary key to a null value!"; int length = keyDecoder.offset() - offset - 1; /* * A note about sort order: * * We are in the primary key section, which means that the element is ordered in * ASCENDING order. In an ideal world, that wouldn't matter because */ accumulate( keyAccumulator, indexKeyPos, table.getFormatIds(sourceKeyColumnPos), index.getDescColumns(indexKeyPos), keyDecoder.array(), offset, length); } } } /* * Handle non-null index columns from the source tables non-primary key columns. * * this will set indexed columns with values taken from the incoming mutation (rather than * backfilling them with existing values, which would occur elsewhere). */ EntryDecoder rowDecoder = getSrcValueDecoder(); rowDecoder.set(mutation.getValue()); BitIndex bitIndex = rowDecoder.getCurrentIndex(); MultiFieldDecoder rowFieldDecoder = rowDecoder.getEntryDecoder(); for (int i = bitIndex.nextSetBit(0); i >= 0; i = bitIndex.nextSetBit(i + 1)) { if (!indexedCols.get(i)) { // skip non-indexed columns rowDecoder.seekForward(rowFieldDecoder, i); continue; } int keyColumnPos = i < mainColToIndexPosMap.length ? mainColToIndexPosMap[i] : -1; if (keyColumnPos < 0) { rowDecoder.seekForward(rowFieldDecoder, i); } else { int offset = rowFieldDecoder.offset(); boolean isNull = rowDecoder.seekForward(rowFieldDecoder, i); hasNullKeyFields = isNull || hasNullKeyFields; int length; if (!isNull) { length = rowFieldDecoder.offset() - offset - 1; accumulate( keyAccumulator, keyColumnPos, table.getFormatIds(i), index.getDescColumns(keyColumnPos), rowFieldDecoder.array(), offset, length); } else { /* * because the field is NULL and it's source is the incoming mutation, we * still need to accumulate it. We must be careful, however, to accumulate the * proper null value. * * In theory, we could use a sparse encoding here--just accumulate a length 0 entry, * which will allow us to use a very short row key to determine nullity. However, that * doesn't work correctly, because doubles and floats at the end of the index might decode * the row key as a double, resulting in goofball answers. * * Instead, we must use the dense encoding approach here. That means that we must * select the proper dense type based on columnTypes[i]. For most data types, this is still * a length-0 array, but for floats and doubles it will put the proper type into place. */ accumulateNull(keyAccumulator, keyColumnPos, table.getFormatIds(i)); } } } /* * Handle NULL index columns from the source tables non-primary key columns. */ for (int srcColIndex = 0; srcColIndex < mainColToIndexPosMap.length; srcColIndex++) { /* position of the source column within the index encoding */ int indexColumnPosition = mainColToIndexPosMap[srcColIndex]; if (!isSourceColumnPrimaryKey(srcColIndex) && indexColumnPosition >= 0 && !bitIndex.isSet(srcColIndex)) { hasNullKeyFields = true; keyAccumulator.add(indexColumnPosition, new byte[] {}, 0, 0); } } // add the row key to the end of the index key byte[] srcRowKey = Encoding.encodeBytesUnsorted(mutation.getRowKey()); EntryEncoder rowEncoder = getRowEncoder(); MultiFieldEncoder entryEncoder = rowEncoder.getEntryEncoder(); entryEncoder.reset(); entryEncoder.setRawBytes(srcRowKey); byte[] indexValue = rowEncoder.encode(); byte[] indexRowKey; if (index.getUnique()) { boolean nonUnique = index.getUniqueWithDuplicateNulls() && (hasNullKeyFields || !keyAccumulator.isFinished()); indexRowKey = getIndexRowKey(srcRowKey, nonUnique); } else indexRowKey = getIndexRowKey(srcRowKey, true); return new KVPair(indexRowKey, indexValue, mutation.getType()); }