All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oracle.kv.impl.api.table.TableKey Maven / Gradle / Ivy

Go to download

NoSQL Database Server - supplies build and runtime support for the server (store) side of the Oracle NoSQL Database.

There is a newer version: 18.3.10
Show newest version
/*-
 * Copyright (C) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle NoSQL
 * Database made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/nosqldb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle NoSQL Database for a copy of the license and
 * additional information.
 */

package oracle.kv.impl.api.table;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import oracle.kv.Key;
import oracle.kv.table.FieldRange;
import oracle.kv.table.Row;
import oracle.kv.table.Table;

import oracle.kv.impl.api.table.ValueSerializer.FieldValueSerializer;
import oracle.kv.impl.api.table.ValueSerializer.RowSerializer;
import oracle.kv.impl.util.ArrayPosition;

/**
 * A class to encapsulate a Key created from a Row or a PrimaryKey in a Table.
 * The algorithm is to iterate the target table's primary key adding fields and
 * static table ids as they are encountered, "left to right."  Addition of key
 * components ends when either (1) the entire primary key is added or (2)
 * a primary key field is missing from the Row.  In the latter case the
 * boolean allowPartial must be true allowing a partial primary key. If either
 * (1) or (2) is true, this.done is set to true.
 *
 * In the case of partial keys it is important to get as many key components
 * as possible to get the most specific parent key for the iteration possible.
 */
public class TableKey {
    final private ArrayList major;
    final private ArrayList minor;
    final private ArrayPosition pkIterator;
    final private Iterator majorIterator;
    final private boolean allowPartial;
    final private RowSerializer row;
    final private TableImpl table;
    private Key key;
    private boolean majorComplete;
    private boolean keyComplete;
    private boolean done;
    private ArrayList current;

    private TableKey(TableImpl table,
                     RowSerializer row,
                     boolean allowPartial) {
        this.row = row;
        this.allowPartial = allowPartial;
        major = new ArrayList();
        minor = new ArrayList();
        pkIterator = new ArrayPosition(table.getPrimaryKey().size());
        majorIterator = table.getShardKey().iterator();
        current = major;
        keyComplete = true;
        this.table = table;
    }

    /**
     * Create a Key for a record based on its value
     * (tableId, primaryKey)
     *
     * If record is empty this is a table iteration on a top-level
     * table -- validate that.
     */
    public static TableKey createKey(Table table, Row row,
                                     boolean allowPartial) {
        return createKeyInternal(table, (RowSerializer)row, allowPartial);
    }

    static TableKey createKeyInternal(Table table,
                                      RowSerializer row,
                                      boolean allowPartial) {
        if (row.size() == 0) {
            if (!allowPartial) {
                throw new IllegalArgumentException("Primary key is empty");
            }
            return new TableKey((TableImpl) table, row, allowPartial).create();
        }
        return new TableKey((TableImpl) table, row, allowPartial).create();
    }

    public TableKey create() {
        createPrimaryKey(table);
        key = Key.createKey(major, minor);
        return this;
    }

    TableImpl getTable() {
        return table;
    }

    public boolean getKeyComplete() {
        return keyComplete;
    }

    public boolean getMajorKeyComplete() {
        return majorComplete;
    }

    public Key getKey() {
        return key;
    }

    RowImpl getRow() {
        return (row instanceof RowImpl) ? (RowImpl)row : null;
    }

    public byte[] getKeyBytes() {
        return key.toByteArray();
    }

    /**
     * Returns the key size, skip the first component if skipFirst is true.
     */
    public int getKeySize(boolean skipFirst) {
        int totalLen = 0;
        for (String component : major) {
            if (skipFirst) {
                skipFirst = false;
                continue;
            }
            totalLen += component.length();
        }
        for (String component : minor) {
            totalLen += component.length();
        }
        return totalLen;
    }

    /*
     * If the major key has been consumed, move to minor array.
     */
    private void incrementMajor() {
        if (majorIterator.hasNext()) {
            majorIterator.next();
        } else {
            current = minor;
            majorComplete = true;
        }
    }

    /*
     * Initialize the state of this object's major and minor lists
     * so that a Key can be created from them for use in table and
     * store operations.
     *
     * The key may be complete or partial (if allowPartial is true).
     * If partial the key fields are added "in order" left to right,
     * stopping when a missing field is encountered.
     */
    private void createPrimaryKey(TableImpl currentTable) {

        if (currentTable.getParent() != null) {
            createPrimaryKey((TableImpl) currentTable.getParent());
        }

        /*
         * If not done yet, add the table's static id and then continue
         * processing the pri key columns that apply to currentTable only.
         * Even if done is true, we must still get into the while-loop below
         * to make sure that there are not other prim key columns set into
         * this.row.
         */
        if (!done){
            current.add(currentTable.getIdString());
        }

        int lastPrimKeyCol = currentTable.getPrimaryKeySize() - 1;

        while (pkIterator.hasNext()) {

            int pos = pkIterator.next();

            /* The position within "row" of the current pk column */
            int pkFieldPos = (row.isPrimaryKey() ?
                              pos :
                              table.getPrimKeyPositions()[pos]);

            if (!done) {
                incrementMajor();
            }

            /*
             * Add the field to the appropriate key array.
             */
            FieldValueSerializer value = row.get(pkFieldPos);

            if (value != null) {

                if (!keyComplete) {
                    throw new IllegalArgumentException(
                        "A required field is missing from the Primary Key");
                }

                String keyStr = formatToKey(value,
                    currentTable.getPrimaryKeySize(pos));
                current.add(keyStr);
            } else {
                keyComplete = false;

                if (!allowPartial) {
                    String fname = table.getRowDef().getFieldName(pkFieldPos);
                    throw new IllegalArgumentException(
                        "Missing primary key field: " + fname);
                }

                /* TODO: check for complete keys for parent tables */
                done = true;

                /*
                 * Continue iterating over the remaining prim key columns to
                 * check that none of them is set.
                 */
            }

            /*
             * In the case of a parent table the parent's primary key will
             * run out before that of the target table.  Just consume the
             * fields that belong to the parent.
             */
            if (pos == lastPrimKeyCol) {
                if (!done) {
                    incrementMajor();
                }
                break;
            }
        }
    }

    /*
     * Return a String representation of the value suitable for use as part of
     * a primary key for the specified value.
     */
    private String formatToKey(FieldValueSerializer value, int keyLen) {

        FieldDefImpl def = (FieldDefImpl)value.getDefinition();
        switch (def.getType()) {
        case BOOLEAN:
            return BooleanValueImpl.toKeyString(value.getBoolean());
        case INTEGER:
            return IntegerValueImpl.toKeyString(value.getInt(), def, keyLen);
        case LONG:
            return LongValueImpl.toKeyString(value.getLong(), def);
        case STRING:
            return StringValueImpl.toKeyString(value.getString());
        case ENUM:
            return EnumValueImpl.toKeyString((EnumDefImpl)def,
                ((EnumDefImpl)def).indexOf(value.getEnumString()));
        case DOUBLE:
            return DoubleValueImpl.toKeyString(value.getDouble());
        case FLOAT:
            return FloatValueImpl.toKeyString(value.getFloat());
        case NUMBER:
            return NumberValueImpl.toKeyString(value.getNumberBytes());
        case TIMESTAMP:
            return TimestampValueImpl.toKeyString(value.getTimestampBytes());
        default:
            throw new IllegalArgumentException
            ("Invalid type for primary key " + def.getType());
        }
    }

    /**
     * Ensure that if the FieldRange is non-null that the field is
     * the "next" one after the last field specified in the primary key.
     *
     * The size of the PrimaryKey (row) should be identical to the index
     * of the field in the primary key list.  That is, if the field is
     * the 3rd field of the key, the first two should be present making
     * the size of the row 2 -- the same as the index of the 3rd field.
     */
    void validateFieldOrder(FieldRange range) {

        if (range != null) {
            List primaryKey = table.getPrimaryKey();
            int index = primaryKey.indexOf(range.getFieldName());
            if (index < 0) {
                throw new IllegalArgumentException
                    ("Field is not part of primary key: " +
                     range.getFieldName());
            }
            if (row.size() < index) {
                throw new IllegalArgumentException
                    ("PrimaryKey is missing fields more significant than" +
                     " field: " + range.getFieldName());
            }
            if (row.size() > index) {
                throw new IllegalArgumentException
                    ("PrimaryKey has extra fields beyond" +
                     " field: " + range.getFieldName());
            }
        }
    }

    void validateFields() {
        if (row.isPrimaryKey()) {
            RecordValueImpl.validIndexFields(row, row.getClassNameForError());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy