Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IGNITE-14692 Validate tuple for STRICT schema #123

Merged
merged 24 commits into from May 20, 2021
7 changes: 7 additions & 0 deletions modules/api/src/main/java/org/apache/ignite/table/Tuple.java
Expand Up @@ -27,6 +27,13 @@
* Provides specialized method for some value-types to avoid boxing/unboxing.
*/
public interface Tuple {
/**
* Returns {@code true} if this tuple contains a column with the specified name.
*
* @return {@code true} if this tuple contains a column with the specified name. Otherwise returns {@code false}.
*/
boolean contains(String colName);

/**
* Gets column value for given column name.
*
Expand Down
Expand Up @@ -23,26 +23,16 @@
* A fixed-sized type representing a bitmask of <code>n</code> bits. The actual size of a bitmask will round up
* to the smallest number of bytes required to store <code>n</code> bits.
*/
public class Bitmask extends NativeType {
public class BitmaskNativeType extends NativeType {
/** */
private final int bits;

/**
* Factory method for creating the bitmask type.
*
* @param nBits Maximum number of bits in the bitmask.
* @return Bitmask type.
*/
public static Bitmask of(int nBits) {
return new Bitmask(nBits);
}

/**
* Creates a bitmask type of size <code>bits</code>. In row will round up to the closest full byte.
*
* @param bits The number of bits in the bitmask.
*/
protected Bitmask(int bits) {
protected BitmaskNativeType(int bits) {
super(NativeTypeSpec.BITMASK, (bits + 7) / 8);

this.bits = bits;
Expand All @@ -63,7 +53,7 @@ public int bits() {
if (o == null || getClass() != o.getClass())
return false;

Bitmask that = (Bitmask)o;
BitmaskNativeType that = (BitmaskNativeType)o;

return bits == that.bits;
}
Expand All @@ -79,7 +69,7 @@ public int bits() {

if (res == 0) {
// The passed in object is also a bitmask, compare the number of bits.
Bitmask that = (Bitmask)o;
BitmaskNativeType that = (BitmaskNativeType)o;

return Integer.compare(bits, that.bits);
}
Expand All @@ -89,6 +79,6 @@ public int bits() {

/** {@inheritDoc} */
@Override public String toString() {
return S.toString(Bitmask.class.getSimpleName(), "bits", bits, "typeSpec", spec(), "len", length());
return S.toString(BitmaskNativeType.class.getSimpleName(), "bits", bits, "typeSpec", spec(), "len", sizeInBytes());
}
}
Expand Up @@ -17,7 +17,9 @@

package org.apache.ignite.internal.schema;

import java.util.function.Supplier;
import org.apache.ignite.internal.tostring.S;
import org.jetbrains.annotations.NotNull;

/**
* Column description for a type schema. Column contains a column name, a column type and a nullability flag.
Expand All @@ -26,6 +28,9 @@
* flag is not taken into account when columns are compared.
*/
public class Column implements Comparable<Column> {
/** {@code Null} value supplier. */
private static final Supplier<Object> NULL_DEFAULT_SUPPLIER = () -> null;

/** Absolute index in schema descriptor. */
private final int schemaIndex;

Expand All @@ -44,6 +49,11 @@ public class Column implements Comparable<Column> {
*/
private final boolean nullable;

/**
* Default value supplier.
*/
private final Supplier<Object> defValSup;

/**
* @param name Column name.
* @param type An instance of column data type.
Expand All @@ -54,7 +64,22 @@ public Column(
NativeType type,
boolean nullable
) {
this(-1, name, type, nullable);
this(-1, name, type, nullable, NULL_DEFAULT_SUPPLIER);
}

/**
* @param name Column name.
* @param type An instance of column data type.
* @param nullable If {@code false}, null values will not be allowed for this column.
* @param defValSup Default value supplier.
*/
public Column(
String name,
NativeType type,
boolean nullable,
@NotNull Supplier<Object> defValSup
) {
this(-1, name, type, nullable, defValSup);
}

/**
Expand All @@ -67,12 +92,14 @@ public Column(
int schemaIndex,
String name,
NativeType type,
boolean nullable
boolean nullable,
@NotNull Supplier<Object> defValSup
) {
this.schemaIndex = schemaIndex;
this.name = name;
this.type = type;
this.nullable = nullable;
this.defValSup = defValSup;
}

/**
Expand Down Expand Up @@ -103,6 +130,19 @@ public boolean nullable() {
return nullable;
}

/**
* Get default value for the column.
*
* @return Default value.
*/
public Object defaultValue() {
Object val = defValSup.get();

assert nullable || val != null : "Null value is not accepted for not nullable column: [col=" + this + ']';

return val;
}

/** {@inheritDoc} */
@Override public boolean equals(Object o) {
if (this == o)
Expand Down Expand Up @@ -132,6 +172,36 @@ public boolean nullable() {
return name.compareTo(o.name);
}

/**
* Validate the object by column's constraint.
*/
public void validate(Object val) {
if (val == null && !nullable) {
throw new IllegalArgumentException("Failed to set column (null was passed, but column is not nullable): " +
"[col=" + this + ']');
}

NativeType objType = NativeTypes.fromObject(val);

if (objType != null && type.mismatch(objType)) {
throw new InvalidTypeException("Column's type mismatch [" +
"column=" + this +
", expectedType=" + type +
", actualType=" + objType +
", val=" + val + ']');
}
}

/**
* Copy column with new schema index.
*
* @param schemaIndex Column index in the schema.
* @return Column.
*/
public Column copy(int schemaIndex) {
return new Column(schemaIndex, name, type, nullable, defValSup);
}

/** {@inheritDoc} */
@Override public String toString() {
return S.toString(Column.class, this);
Expand Down
Expand Up @@ -195,7 +195,7 @@ private Column[] sortedCopy(int schemaBaseIdx, Column[] cols) {
for (int i = 0; i < cp.length; i++) {
Column c = cp[i];

cp[i] = new Column(schemaBaseIdx + i, c.name(), c.type(), c.nullable());
cp[i] = c.copy(schemaBaseIdx + i);
}

return cp;
Expand Down Expand Up @@ -287,7 +287,7 @@ private int foldManual(int b, int mask) {
", mask=" + mask +
", cols" + Arrays.toString(cols) + ']';

size += cols[idx].type().length();
size += cols[idx].type().sizeInBytes();
}
}

Expand All @@ -309,6 +309,24 @@ public int columnIndex(String colName) {
throw new NoSuchElementException("No field '" + colName + "' defined");
}

/** {@inheritDoc} */
@Override public boolean equals(Object o) {
if (this == o)
return true;

if (o == null || getClass() != o.getClass())
return false;

Columns columns = (Columns)o;

return Arrays.equals(cols, columns.cols);
}

/** {@inheritDoc} */
@Override public int hashCode() {
return Arrays.hashCode(cols);
}

/** {@inheritDoc} */
@Override public String toString() {
return S.arrayToString(cols);
Expand Down
Expand Up @@ -18,59 +18,33 @@
package org.apache.ignite.internal.schema;

import org.apache.ignite.internal.tostring.S;
import org.jetbrains.annotations.NotNull;

/**
* A thin wrapper over {@link NativeTypeSpec} to instantiate parameterized constrained types.
*/
public class NativeType implements Comparable<NativeType> {
/** */
public static final NativeType BYTE = new NativeType(NativeTypeSpec.BYTE, 1);

/** */
public static final NativeType SHORT = new NativeType(NativeTypeSpec.SHORT, 2);

/** */
public static final NativeType INTEGER = new NativeType(NativeTypeSpec.INTEGER, 4);

/** */
public static final NativeType LONG = new NativeType(NativeTypeSpec.LONG, 8);

/** */
public static final NativeType FLOAT = new NativeType(NativeTypeSpec.FLOAT, 4);

/** */
public static final NativeType DOUBLE = new NativeType(NativeTypeSpec.DOUBLE, 8);

/** */
public static final NativeType UUID = new NativeType(NativeTypeSpec.UUID, 16);

/** */
public static final NativeType STRING = new NativeType(NativeTypeSpec.STRING);

/** */
public static final NativeType BYTES = new NativeType(NativeTypeSpec.BYTES);

/** */
private final NativeTypeSpec typeSpec;

/** Type length. */
private final int len;
/** Type size in bytes. */
private final int size;

/**
* Constructor for fixed-length types.
*
* @param typeSpec Type spec.
* @param len Type length.
* @param size Type size in bytes.
*/
protected NativeType(NativeTypeSpec typeSpec, int len) {
protected NativeType(NativeTypeSpec typeSpec, int size) {
if (!typeSpec.fixedLength())
throw new IllegalArgumentException("Size must be provided only for fixed-length types: " + typeSpec);

if (len <= 0)
throw new IllegalArgumentException("Size must be positive [typeSpec=" + typeSpec + ", size=" + len + ']');
if (size <= 0)
throw new IllegalArgumentException("Size must be positive [typeSpec=" + typeSpec + ", size=" + size + ']');

this.typeSpec = typeSpec;
this.len = len;
this.size = size;
}

/**
Expand All @@ -84,17 +58,17 @@ protected NativeType(NativeTypeSpec typeSpec) {
"length-aware constructor: " + typeSpec);

this.typeSpec = typeSpec;
this.len = 0;
this.size = 0;
}

/**
* @return Length of the type if it is a fixlen type. For varlen types the return value is undefined, so the user
* @return Size in bytes of the type if it is a fixlen type. For varlen types the return value is undefined, so the user
* should explicitly check {@code spec().fixedLength()} before using this method.
*
* @see NativeTypeSpec#fixedLength()
*/
public int length() {
return len;
public int sizeInBytes() {
return size;
}

/**
Expand All @@ -104,6 +78,11 @@ public NativeTypeSpec spec() {
return typeSpec;
}

/** */
public boolean mismatch(@NotNull NativeType type) {
return this != type && typeSpec != type.typeSpec;
}

/** {@inheritDoc} */
@Override public boolean equals(Object o) {
if (this == o)
Expand All @@ -114,29 +93,29 @@ public NativeTypeSpec spec() {

NativeType that = (NativeType)o;

return len == that.len && typeSpec == that.typeSpec;
return size == that.size && typeSpec == that.typeSpec;
}

/** {@inheritDoc} */
@Override public int hashCode() {
int res = typeSpec.hashCode();

res = 31 * res + len;
res = 31 * res + size;

return res;
}

/** {@inheritDoc} */
@Override public int compareTo(NativeType o) {
// Fixed-sized types go first.
if (len <= 0 && o.len > 0)
if (size <= 0 && o.size > 0)
return 1;

if (len > 0 && o.len <= 0)
if (size > 0 && o.size <= 0)
return -1;

// Either size is -1 for both, or positive for both. Compare sizes, then description.
int cmp = Integer.compare(len, o.len);
int cmp = Integer.compare(size, o.size);

if (cmp != 0)
return cmp;
Expand All @@ -148,7 +127,8 @@ public NativeTypeSpec spec() {
@Override public String toString() {
return S.toString(NativeType.class.getSimpleName(),
"name", typeSpec.name(),
"len", len,
"sizeInBytes", size,
"fixed", typeSpec.fixedLength());
}

}