Java源码示例:org.apache.phoenix.schema.types.PDataType

示例1
@Override
public Expression visitLeave(CastParseNode node, List<Expression> children) throws SQLException {
    ParseNode childNode = node.getChildren().get(0);
    PDataType targetDataType = node.getDataType();
    Expression childExpr = children.get(0);
    PDataType fromDataType = childExpr.getDataType();
    
    if (childNode instanceof BindParseNode) {
        context.getBindManager().addParamMetaData((BindParseNode)childNode, childExpr);
    }
    
    Expression expr = childExpr;
    if(fromDataType != null) {
        /*
         * IndexStatementRewriter creates a CAST parse node when rewriting the query to use
         * indexed columns. Without this check present we wrongly and unnecessarily
         * end up creating a RoundExpression. 
         */
        if (context.getCurrentTable().getTable().getType() != PTableType.INDEX) {
            expr =  convertToRoundExpressionIfNeeded(fromDataType, targetDataType, children);
        }
    }
    boolean rowKeyOrderOptimizable = context.getCurrentTable().getTable().rowKeyOrderOptimizable();
    return wrapGroupByExpression(CoerceExpression.create(expr, targetDataType, SortOrder.getDefault(), expr.getMaxLength(), rowKeyOrderOptimizable));  
}
 
示例2
@Override
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
    byte[] result = ByteUtil.EMPTY_BYTE_ARRAY;
    for (int i=0; i<children.size(); i++) {
        if (children.get(i).getDataType() == null || !children.get(i).evaluate(tuple, ptr)) {
            continue;
        }
        PDataType childType = children.get(i).getDataType();
        SortOrder sortOrder = children.get(i).getSortOrder();
        // We could potentially not invert the bytes, but we might as well since we're allocating
        // additional space here anyway.
        if (childType.isCoercibleTo(PVarchar.INSTANCE)) {
            result = ByteUtil.concat(result, ByteUtil.concat(sortOrder, ptr));
        } else {
            result = ByteUtil.concat(result, PVarchar.INSTANCE.toBytes(childType.toObject(ptr, sortOrder).toString()));
        }
    }
    ptr.set(result);
    return true;
}
 
示例3
@Override
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
    Expression child = children.get(0);
    if (!child.evaluate(tuple, ptr)) {
        return false;
    }
    if (ptr.getLength() == 0) {
        return true;
    }
    int sqlType = child.getDataType().getCodec().decodeInt(ptr, child.getSortOrder());
    try {
        byte[] sqlTypeNameBytes = PDataType.fromTypeId(sqlType).getSqlTypeNameBytes();
        ptr.set(sqlTypeNameBytes);
    } catch (IllegalDataException e) {
        ptr.set(ByteUtil.EMPTY_BYTE_ARRAY);
    }
    return true;
}
 
示例4
/**
 * Imperfect estimate of row size given a PTable
 * TODO: keep row count in stats table and use total size / row count instead
 * @param table
 * @return estimate of size in bytes of a row
 */
public static long estimateRowSize(PTable table) {
	int keyLength = estimateKeyLength(table);
	long rowSize = 0;
	for (PColumn column : table.getColumns()) {
		if (!SchemaUtil.isPKColumn(column)) {
            PDataType type = column.getDataType();
            Integer maxLength = column.getMaxLength();
            int valueLength = !type.isFixedWidth() ? VAR_KV_LENGTH_ESTIMATE : maxLength == null ? type.getByteSize() : maxLength;
			rowSize += KeyValue.getKeyValueDataStructureSize(keyLength, column.getFamilyName().getBytes().length, column.getName().getBytes().length, valueLength);
		}
	}
	byte[] emptyKeyValueKV = EncodedColumnsUtil.getEmptyKeyValueInfo(table).getFirst();
	// Empty key value
	rowSize += KeyValue.getKeyValueDataStructureSize(keyLength, getEmptyColumnFamily(table).length, emptyKeyValueKV.length, 0);
	return rowSize;
}
 
示例5
@Override
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
    Expression expression = getChildExpression();
    if (!expression.evaluate(tuple, ptr)) {
        return false;
    }
    if ( ptr.getLength() == 0) {
        return true; //means null
    }
    long dateTime = inputCodec.decodeLong(ptr, expression.getSortOrder());
    int minute = (int)(((dateTime/1000) % 3600)/60);
    PDataType returnType = getDataType();
    byte[] byteValue = new byte[returnType.getByteSize()];
    returnType.getCodec().encodeInt(minute, byteValue, 0);
    ptr.set(byteValue);
    return true;
}
 
示例6
public static Expression create (List<Expression> children) throws SQLException {
    Expression firstChild = children.get(0);
    PDataType firstChildDataType = firstChild.getDataType();
    String timeUnit = (String)((LiteralExpression)children.get(1)).getValue();
    LiteralExpression multiplierExpr = (LiteralExpression)children.get(2);
    
    /*
     * When rounding off timestamp to milliseconds, nanos play a part only when the multiplier value
     * is equal to 1. This is because for cases when multiplier value is greater than 1, number of nanos/multiplier
     * will always be less than half the nanos in a millisecond. 
     */
    if((timeUnit == null || TimeUnit.MILLISECOND.toString().equalsIgnoreCase(timeUnit)) && ((Number)multiplierExpr.getValue()).intValue() == 1) {
        return new RoundTimestampExpression(children);
    }
    // Coerce TIMESTAMP to DATE, as the nanos has no affect
    List<Expression> newChildren = Lists.newArrayListWithExpectedSize(children.size());
    newChildren.add(CoerceExpression.create(firstChild, firstChildDataType == PTimestamp.INSTANCE ?
        PDate.INSTANCE : PUnsignedDate.INSTANCE));
    newChildren.addAll(children.subList(1, children.size()));
    return RoundDateExpression.create(newChildren);
}
 
示例7
@Override
public String getString(int columnIndex) throws SQLException {
    checkCursorState();
    // Get the value using the expected type instead of trying to coerce to VARCHAR.
    // We can't coerce using our formatter because we don't have enough context in PDataType.
    ColumnProjector projector = rowProjector.getColumnProjector(columnIndex-1);
    PDataType type = projector.getExpression().getDataType();
    Object value = projector.getValue(currentRow,type, ptr);
    if (wasNull = (value == null)) {
        return null;
    }
    // Run Object through formatter to get String.
    // This provides a simple way of getting a reasonable string representation
    // for types like DATE and TIME
    Format formatter = statement.getFormatter(type);
    return formatter == null ? value.toString() : formatter.format(value);
}
 
示例8
@Test
public void testForCorrectSeparatorBytes4() throws Exception {
    Object[] o1 = new Object[]{"a", "b", null};
    Object[] o2 = new Object[]{null, "c", "d", "e"};
    PDataType type = PVarcharArray.INSTANCE;
    PDataType base = PVarchar.INSTANCE;

    PhoenixArray arr1 = new PhoenixArray(base, o1);
    PhoenixArray arr2 = new PhoenixArray(base, o2);
    LiteralExpression array1Literal, array2Literal;
    array1Literal = LiteralExpression.newConstant(arr1, type, null, null, SortOrder.ASC, Determinism.ALWAYS);
    array2Literal = LiteralExpression.newConstant(arr2, type, null, null, SortOrder.DESC, Determinism.ALWAYS);
    List<Expression> expressions = Lists.newArrayList((Expression) array1Literal);
    expressions.add(array2Literal);

    Expression arrayConcatFunction = new ArrayConcatFunction(expressions);
    ImmutableBytesWritable ptr = new ImmutableBytesWritable();
    arrayConcatFunction.evaluate(null, ptr);
    byte[] expected = new byte[]{97, 0, 98, 0, 0, -2, 99, 0, 100, 0, 101, 0, 0, 0, -128, 1, -128, 3, -128, 5, -128, 5, -128, 7, -128, 9, -128, 11, 0, 0, 0, 14, 0, 0, 0, 7, 1};
    assertArrayEquals(expected, ptr.get());
}
 
示例9
private void appendPKColumnValue(StringBuilder buf, byte[] range, Boolean isNull, int slotIndex) {
    if (Boolean.TRUE.equals(isNull)) {
        buf.append("null");
        return;
    }
    if (Boolean.FALSE.equals(isNull)) {
        buf.append("not null");
        return;
    }
    if (range.length == 0) {
        buf.append('*');
        return;
    }
    ScanRanges scanRanges = context.getScanRanges();
    PDataType type = scanRanges.getSchema().getField(slotIndex).getDataType();
    SortOrder sortOrder = tableRef.getTable().getPKColumns().get(slotIndex).getSortOrder();
    if (sortOrder == SortOrder.DESC) {
        buf.append('~');
        range = SortOrder.invert(range, 0, new byte[range.length], 0, range.length);
    }
    Format formatter = context.getConnection().getFormatter(type);
    buf.append(type.toStringLiteral(range, formatter));
}
 
示例10
protected Map<Object, Integer> getSortedValueVsCount(final boolean ascending, final PDataType type) {
    // To sort the valueVsCount
    Comparator<Object> comparator = new Comparator<Object>() {
        @Override
        public int compare(Object o1, Object o2) {
            if (ascending) { 
                return type.compareTo(o1, o2); 
            }
            return type.compareTo(o2, o1);
        }
    };
    Map<Object, Integer> sorted = new TreeMap<Object, Integer>(comparator);
    for (Entry<ImmutableBytesPtr, Integer> entry : valueVsCount.entrySet()) {
        sorted.put(type.toObject(entry.getKey(), sortOrder), entry.getValue());
    }
    return sorted;
}
 
示例11
@Override
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
    Expression expression = getChildExpression();
    if (!expression.evaluate(tuple, ptr)) {
        return false;
    }
    if ( ptr.getLength() == 0) {
        return true; //means null
    }
    long dateTime = inputCodec.decodeLong(ptr, expression.getSortOrder());
    int hour = (int)(((dateTime/1000) % (24*3600))/3600);
    PDataType returnType = getDataType();
    byte[] byteValue = new byte[returnType.getByteSize()];
    returnType.getCodec().encodeInt(hour, byteValue, 0);
    ptr.set(byteValue);
    return true;
}
 
示例12
public static PDataType getIndexColumnDataType(boolean isNullable, PDataType dataType) {
    if (dataType == null || !isNullable || !dataType.isFixedWidth()) {
        return dataType;
    }
    // for fixed length numeric types and boolean
    if (dataType.isCastableTo(PDecimal.INSTANCE)) {
        return PDecimal.INSTANCE;
    }
    // for CHAR
    if (dataType.isCoercibleTo(PVarchar.INSTANCE)) {
        return PVarchar.INSTANCE;
    }

    if (PBinary.INSTANCE.equals(dataType)) {
        return PVarbinary.INSTANCE;
    }
    throw new IllegalArgumentException("Unsupported non nullable type " + dataType);
}
 
示例13
/**
 * Creates a table with the given properties and returns its name. If the table is multi-tenant,
 * also creates a tenant view for that table and returns the name of the view instead.
 * @param baseConn  a non-tenant specific connection. Used to create the base tables
 * @param conn  a tenant-specific connection, if necessary. Otherwise ignored.
 * @param isMultiTenant  whether or not this table should be multi-tenant
 * @param pkType  the data type of the primary key columns
 * @param saltBuckets  the number of salt buckets if the table is salted, otherwise 0
 * @return  the table or view name that should be used to access the created table
 */
private static String initializeAndGetTable(Connection baseConn, Connection conn, boolean isMultiTenant, PDataType pkType, int saltBuckets) throws SQLException {
    String tableName = generateUniqueName() + "in_test" + pkType.getSqlTypeName() + saltBuckets + (isMultiTenant ? "_multi" : "_single");
    String tableDDL = createTableDDL(tableName, pkType, saltBuckets, isMultiTenant);
    baseConn.createStatement().execute(tableDDL);

    // if requested, create a tenant specific view and return the view name instead
    if(isMultiTenant) {
        String viewName = tableName + "_view";
        String viewDDL = "CREATE VIEW " + viewName + " AS SELECT * FROM " + tableName;
        conn.createStatement().execute(viewDDL);
        return viewName;
    }
    else {
        return tableName;
    }
}
 
示例14
private BigDecimal mean() {
    BigDecimal sum = BigDecimal.ZERO;
    for (Entry<ImmutableBytesPtr, Integer> entry : valueVsCount.entrySet()) {
        BigDecimal colValue = (BigDecimal) PDecimal.INSTANCE.toObject(entry.getKey());
        sum = sum.add(colValue.multiply(new BigDecimal(entry.getValue())));
    }
    return sum.divide(new BigDecimal(totalCount), PDataType.DEFAULT_MATH_CONTEXT);
}
 
示例15
@Override
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
    if (children.get(0).evaluate(tuple, ptr)) {
        PDataType dataType = getDataType();
        long time = dataType.getCodec().decodeLong(ptr, children.get(0).getSortOrder());
        long value = roundTime(time);
        
        Date d = new Date(value);
        byte[] byteValue = dataType.toBytes(d);
        ptr.set(byteValue);
        return true;
    }
    return false;
}
 
示例16
public static Expression getRoundExpression(List<Expression> children) throws SQLException {
    final Expression firstChild = children.get(0);
    final PDataType firstChildDataType = firstChild.getDataType();
    
    if(firstChildDataType.isCoercibleTo(PDate.INSTANCE)) {
        return RoundDateExpression.create(children);
    } else if (firstChildDataType.isCoercibleTo(PTimestamp.INSTANCE)) {
        return RoundTimestampExpression.create(children);
    } else if(firstChildDataType.isCoercibleTo(PDecimal.INSTANCE)) {
        return RoundDecimalExpression.create(children);
    } else {
        throw TypeMismatchException.newException(firstChildDataType, "1");
    }
}
 
示例17
public ToNumberFunction(List<Expression> children, StatementContext context) throws SQLException {
    super(children.subList(0, 1));
    PDataType dataType = children.get(0).getDataType();
    String formatString = (String)((LiteralExpression)children.get(1)).getValue(); // either date or number format string
    Format formatter =  null;
    FunctionArgumentType type;

    if (dataType.isCoercibleTo(PTimestamp.INSTANCE)) {
        if (formatString == null) {
            formatString = context.getDateFormat();
            formatter = context.getDateFormatter();
        } else {
            formatter = FunctionArgumentType.TEMPORAL.getFormatter(formatString);
        }
        type = FunctionArgumentType.TEMPORAL;
    }
    else if (dataType.isCoercibleTo(PChar.INSTANCE)) {
        if (formatString != null) {
            formatter = FunctionArgumentType.CHAR.getFormatter(formatString);
        }
        type = FunctionArgumentType.CHAR;
    }
    else {
        throw new SQLException(dataType + " type is unsupported for TO_NUMBER().  Numeric and temporal types are supported.");
    }
    Preconditions.checkNotNull(type);
    this.type = type;
    this.formatString = formatString;
    this.format = formatter;
}
 
示例18
private static String getDefaultFormat(PDataType type) {
    int ordinal = type.ordinal();
    if (ordinal >= 0 || ordinal < defaultPattern.length) {
        String format = defaultPattern[ordinal];
        if (format != null) {
            return format;
        }
    }
    throw new IllegalArgumentException("Expected a date/time type, but got " + type);
}
 
示例19
public static Expression getCeilExpression(List<Expression> children) throws SQLException {
    final Expression firstChild = children.get(0);
    final PDataType firstChildDataType = firstChild.getDataType();
    if(firstChildDataType.isCoercibleTo(PDate.INSTANCE)) {
        return CeilDateExpression.create(children);
    } else if (firstChildDataType == PTimestamp.INSTANCE || firstChildDataType == PUnsignedTimestamp.INSTANCE) {
        return CeilTimestampExpression.create(children);
    } else if(firstChildDataType.isCoercibleTo(PDecimal.INSTANCE)) {
        return CeilDecimalExpression.create(children);
    } else {
        throw TypeMismatchException.newException(firstChildDataType, "1");
    }
}
 
示例20
@Test
public void testArrayRemoveFunction4() throws Exception {
	Object[] o = new Object[] { "1", "2", "2", "4" };
	Object[] o2 = new Object[] { "1", "2", "2", "4" };
	Object element = "5";
	PDataType baseType = PVarchar.INSTANCE;

	PhoenixArray arr = new PhoenixArray(baseType, o);
	PhoenixArray expected = new PhoenixArray(baseType, o2);
	test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null,
			baseType, null, null, expected, SortOrder.ASC, SortOrder.ASC);
}
 
示例21
private static Collection<?> foreach(KeyRange[][] ranges, int[] widths, byte[] lowerInclusive,
        byte[] upperExclusive, KeyRange[][] expectedRanges) {
    List<List<KeyRange>> slots = Lists.transform(Lists.newArrayList(ranges), ARRAY_TO_LIST);
    List<List<KeyRange>> expectedSlots = expectedRanges == null ? null : Lists.transform(Lists.newArrayList(expectedRanges), ARRAY_TO_LIST);
    RowKeySchemaBuilder builder = new RowKeySchemaBuilder(10);
    for (final int width: widths) {
        builder.addField(
                new PDatum() {
                    @Override
                    public boolean isNullable() {
                        return width <= 0;
                    }
                    @Override
                    public PDataType getDataType() {
                        return width <= 0 ? PVarchar.INSTANCE : PChar.INSTANCE;
                    }
                   @Override
                    public Integer getMaxLength() {
                        return width <= 0 ? null : width;
                    }
                    @Override
                    public Integer getScale() {
                        return null;
                    }
                    @Override
                    public SortOrder getSortOrder() {
                        return SortOrder.getDefault();
                    }
                }, width <= 0, SortOrder.getDefault());
    }
    List<Object> ret = Lists.newArrayList();
    ret.add(new Object[] {slots, builder.build(), lowerInclusive, upperExclusive, expectedSlots});
    return ret;
}
 
示例22
public LiteralParseNode literal(Object value, PDataType expectedType) throws SQLException {
    PDataType actualType = PDataType.fromLiteral(value);
    if (actualType != null && actualType != expectedType) {
        checkTypeMatch(expectedType, actualType);
        value = expectedType.toObject(value, actualType);
    }
    return new LiteralParseNode(value);
    /*
    Object typedValue = expectedType.toObject(value.toString());
    return new LiteralParseNode(typedValue);
    */
}
 
示例23
@Override
public FunctionExpression create(List<Expression> children, StatementContext context) throws SQLException {
    PDataType dataType = children.get(0).getDataType();
    String formatString = (String)((LiteralExpression)children.get(1)).getValue(); // either date or number format string
    Format formatter =  null;
    FunctionArgumentType type;
    
    if (dataType.isCoercibleTo(PTimestamp.INSTANCE)) {
        if (formatString == null) {
            formatString = context.getDateFormat();
            formatter = context.getDateFormatter();
        } else {
            formatter = FunctionArgumentType.TEMPORAL.getFormatter(formatString);
        }
        type = FunctionArgumentType.TEMPORAL;
    }
    else if (dataType.isCoercibleTo(PChar.INSTANCE)) {
        if (formatString != null) {
            formatter = FunctionArgumentType.CHAR.getFormatter(formatString);
        }
        type = FunctionArgumentType.CHAR;
    }
    else {
        throw new SQLException(dataType + " type is unsupported for TO_NUMBER().  Numeric and temporal types are supported.");
    }
    return new ToNumberFunction(children, type, formatString, formatter);
}
 
示例24
public static void validateFunctionArguement(BuiltInFunctionInfo info,
        int childIndex, Expression child)
        throws ArgumentTypeMismatchException, ValueRangeExcpetion {
    BuiltInFunctionArgInfo arg = info.getArgs()[childIndex];
    if (arg.getAllowedTypes().length > 0) {
        boolean isCoercible = false;
        for (Class<? extends PDataType> type :arg.getAllowedTypes()) {
            if (child.getDataType().isCoercibleTo(
                PDataTypeFactory.getInstance().instanceFromClass(type))) {
                isCoercible = true;
                break;
            }
        }
        if (!isCoercible) {
            throw new ArgumentTypeMismatchException(arg.getAllowedTypes(),
                child.getDataType(), info.getName() + " argument " + (childIndex + 1));
        }
        if (child instanceof LiteralExpression) {
            LiteralExpression valueExp = (LiteralExpression) child;
            LiteralExpression minValue = arg.getMinValue();
            LiteralExpression maxValue = arg.getMaxValue();
            if (minValue != null && minValue.getDataType().compareTo(minValue.getValue(), valueExp.getValue(), valueExp.getDataType()) > 0) {
                throw new ValueRangeExcpetion(minValue, maxValue == null ? "" : maxValue, valueExp.getValue(), info.getName() + " argument " + (childIndex + 1));
            }
            if (maxValue != null && maxValue.getDataType().compareTo(maxValue.getValue(), valueExp.getValue(), valueExp.getDataType()) < 0) {
                throw new ValueRangeExcpetion(minValue == null ? "" : minValue, maxValue, valueExp.getValue(), info.getName() + " argument " + (childIndex + 1));
            }
        }
    }
    if (arg.isConstant() && ! (child instanceof LiteralExpression) ) {
        throw new ArgumentTypeMismatchException("constant", child.toString(), info.getName() + " argument " + (childIndex + 1));
    }
    if (!arg.getAllowedValues().isEmpty()) {
        Object value = ((LiteralExpression)child).getValue();
        if (!arg.getAllowedValues().contains(value.toString().toUpperCase())) {
            throw new ArgumentTypeMismatchException(Arrays.toString(arg.getAllowedValues().toArray(new String[0])),
                    value.toString(), info.getName() + " argument " + (childIndex + 1));
        }
    }
}
 
示例25
@Test
public void testCharPaddingDesc3() throws SQLException {
    PDataType dataType = PChar.INSTANCE;
    String str = "phoenix";
    byte[] result = new byte[]{-113, -105, -112, -102, -111, -106, -121};
    test(str, dataType, 7, SortOrder.DESC, result);
}
 
示例26
public static ResourceSchema getResourceSchema(final Configuration configuration) throws IOException {
    
    final ResourceSchema schema = new ResourceSchema();
    try {
        List<ColumnInfo> columns = null;
        final SchemaType schemaType = PhoenixConfigurationUtil.getSchemaType(configuration);
        if(SchemaType.QUERY.equals(schemaType)) {
            final String sqlQuery = PhoenixConfigurationUtil.getSelectStatement(configuration);
            Preconditions.checkNotNull(sqlQuery, "No Sql Query exists within the configuration");
            final SqlQueryToColumnInfoFunction function = new SqlQueryToColumnInfoFunction(configuration);
            columns = function.apply(sqlQuery);
        } else {
            columns = PhoenixConfigurationUtil.getSelectColumnMetadataList(configuration);
        }
        ResourceFieldSchema fields[] = new ResourceFieldSchema[columns.size()];
        int i = 0;
        for(ColumnInfo cinfo : columns) {
            int sqlType = cinfo.getSqlType();
            PDataType phoenixDataType = PDataType.fromTypeId(sqlType);
            byte pigType = TypeUtil.getPigDataTypeForPhoenixType(phoenixDataType);
            ResourceFieldSchema field = new ResourceFieldSchema();
            field.setType(pigType).setName(cinfo.getDisplayName());
            fields[i++] = field;
        }
        schema.setFields(fields);    
    } catch(SQLException sqle) {
        LOG.error(String.format("Error: SQLException [%s] ",sqle.getMessage()));
        throw new IOException(sqle);
    }
    
    return schema;
}
 
示例27
public static TypeMismatchException newException(PDataType lhs, PDataType rhs, String location)  {
    return new TypeMismatchException(getMessage(lhs,rhs,location));
}
 
示例28
@Override
public PDataType getDataType() {
    return PDouble.INSTANCE;
}
 
示例29
@Override
public PDataType getDataType() {
    return PVarchar.INSTANCE;
}
 
示例30
public ParameterizedScanUtilTest(List<List<KeyRange>> slots, int[] widths, byte[] expectedKey, Bound bound)
        throws Exception {
    RowKeySchemaBuilder builder = new RowKeySchemaBuilder(widths.length);
    for (final int width : widths) {
        if (width > 0) {
            builder.addField(new PDatum() {
                @Override public boolean isNullable() {
                    return false;
                }

                @Override public PDataType getDataType() {
                    return PChar.INSTANCE;
                }

                @Override public Integer getMaxLength() {
                    return width;
                }

                @Override public Integer getScale() {
                    return null;
                }

                @Override public SortOrder getSortOrder() {
                    return SortOrder.getDefault();
                }
            }, false, SortOrder.getDefault());
        } else {
            builder.addField(new PDatum() {
                @Override public boolean isNullable() {
                    return false;
                }

                @Override public PDataType getDataType() {
                    return PVarchar.INSTANCE;
                }

                @Override public Integer getMaxLength() {
                    return null;
                }

                @Override public Integer getScale() {
                    return null;
                }

                @Override public SortOrder getSortOrder() {
                    return SortOrder.getDefault();
                }
            }, false, SortOrder.getDefault());
        }
    }
    this.schema = builder.build();
    this.slots = slots;
    this.expectedKey = expectedKey;
    this.bound = bound;
}