com.foundationdb.sql.server.ServerValueDecoder.java Source code

Java tutorial

Introduction

Here is the source code for com.foundationdb.sql.server.ServerValueDecoder.java

Source

/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2009-2015 FoundationDB, LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.foundationdb.sql.server;

import com.foundationdb.qp.operator.QueryBindings;
import com.foundationdb.qp.operator.QueryContext;
import com.foundationdb.server.error.AkibanInternalException;
import com.foundationdb.server.error.UnknownDataTypeException;
import com.foundationdb.server.error.UnsupportedCharsetException;
import com.foundationdb.server.types.TCast;
import com.foundationdb.server.types.TExecutionContext;
import com.foundationdb.server.types.TInstance;
import com.foundationdb.server.types.aksql.aktypes.AkBool;
import com.foundationdb.server.types.aksql.aktypes.AkGUID;
import com.foundationdb.server.types.common.types.TBinary;
import com.foundationdb.server.types.common.types.TypesTranslator;
import com.foundationdb.server.types.mcompat.mtypes.MApproximateNumber;
import com.foundationdb.server.types.mcompat.mtypes.MBinary;
import com.foundationdb.server.types.mcompat.mtypes.MDateAndTime;
import com.foundationdb.server.types.mcompat.mtypes.MNumeric;
import com.foundationdb.server.types.mcompat.mtypes.MString;
import com.foundationdb.server.types.service.TypesRegistryService;
import com.foundationdb.server.types.value.Value;
import com.foundationdb.server.types.value.ValueSource;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import java.math.BigDecimal;
import java.sql.Types;
import java.util.Collections;
import java.io.*;

/** Decode values from external representation into query bindings. */
public class ServerValueDecoder {
    private final TypesTranslator typesTranslator;
    private final String encoding;

    public ServerValueDecoder(TypesTranslator typesTranslator, String encoding) {
        this.typesTranslator = typesTranslator;
        this.encoding = encoding;
    }

    /** Decode the given value into a the given bindings at the given position.
     */
    public void decodeValue(byte[] encoded, ServerType type, boolean binary, QueryBindings bindings, int index,
            QueryContext queryContext, TypesRegistryService typesRegistryService) {
        TInstance targetType = type != null ? type.getType() : null;
        if (targetType == null && encoded != null) {
            throw new UnknownDataTypeException(null);
        }
        ValueSource source;
        if (encoded == null) {
            Value value = new Value(targetType);
            value.putNull();
            bindings.setValue(index, value);
            return;
        } else if (!binary) {
            try {
                source = new Value(MString.varchar(), new String(encoded, encoding));
            } catch (UnsupportedEncodingException ex) {
                throw new UnsupportedCharsetException(encoding);
            }
        } else {
            try {
                switch (type.getBinaryEncoding()) {
                case BINARY_OCTAL_TEXT:
                    source = new Value(MBinary.VARBINARY.instance(false), encoded);
                    break;
                case INT_8:
                case INT_16:
                case INT_32:
                case INT_64: // Types.BIGINT
                    // Go by the length sent rather than the implied type.
                    source = decodeIntegerType(encoded);
                    break;
                case FLOAT_32:

                    source = new Value(MApproximateNumber.FLOAT.instance(false),
                            getDataStream(encoded).readFloat());
                    break;
                case FLOAT_64:
                    source = new Value(MApproximateNumber.DOUBLE.instance(false),
                            getDataStream(encoded).readDouble());
                    break;
                case BOOLEAN_C:
                    source = new Value(AkBool.INSTANCE.instance(false), encoded[0] != 0);
                    break;
                case TIMESTAMP_INT64_MICROS_2000_NOTZ: // Types.TIMESTAMP
                    source = decodeTimestampInt64Micros2000NoTZ(encoded);
                    break;
                case UUID:
                    Value value = new Value(AkGUID.INSTANCE.instance(false));
                    value.putObject(AkGUID.bytesToUUID(encoded, 0));
                    source = value;
                    break;
                case STRING_BYTES: {
                    String s = new String(encoded, encoding);
                    source = new Value(MString.VARCHAR.instance(s.length(), false), s);
                    break;
                }
                // Note: these types had previous implementations, but I couldn't exercise them in tests to verify
                // either with jdbc or pg8000. If you run into them, try looking at the log for this file, it most
                // likely has a correct starting point
                case TIMESTAMP_FLOAT64_SECS_2000_NOTZ: // Types.TIMESTAMP
                case DAYS_2000: // DATE
                case TIME_FLOAT64_SECS_NOTZ: // TIME
                case TIME_INT64_MICROS_NOTZ: // TIME
                case DECIMAL_PG_NUMERIC_VAR:
                default:
                    throw new UnknownDataTypeException(type.toString());
                }
            } catch (UnsupportedEncodingException ex) {
                throw new UnsupportedCharsetException(encoding);
            } catch (IOException ex) {
                throw new AkibanInternalException("IO error reading from byte array", ex);
            }
        }
        TCast cast = typesRegistryService.getCastsResolver().cast(source.getType(), targetType);
        TExecutionContext context = new TExecutionContext(Collections.singletonList(source.getType()), targetType,
                queryContext);
        Value target = new Value(targetType);
        cast.evaluate(context, source, target);
        bindings.setValue(index, target);
    }

    private ValueSource decodeTimestampInt64Micros2000NoTZ(byte[] encoded) throws IOException {
        long micros = getDataStream(encoded).readLong();
        long secs = micros / 1000000;
        long milliseconds = seconds2000NoTZ(secs);
        int nanos = (int) (micros - secs * 1000000) * 1000;
        Value source = new Value(MDateAndTime.TIMESTAMP.instance(false));
        typesTranslator.setTimestampMillisValue(source, milliseconds, nanos);
        return source;
    }

    public ValueSource decodeIntegerType(byte[] encoded) throws IOException {
        switch (encoded.length) {
        case 1:
            return new Value(MNumeric.TINYINT.instance(false), getDataStream(encoded).read());
        case 2:
            return new Value(MNumeric.SMALLINT.instance(false), getDataStream(encoded).readShort());
        case 4:
            return new Value(MNumeric.INT.instance(false), getDataStream(encoded).readInt());
        case 8:
            return new Value(MNumeric.BIGINT.instance(false), getDataStream(encoded).readLong());
        default:
            throw new AkibanInternalException("Not an integer size: " + encoded);
        }
    }

    private static DataInputStream getDataStream(byte[] bytes) {
        return new DataInputStream(new ByteArrayInputStream(bytes));
    }

    private static long seconds2000NoTZ(long secs) {
        long millis = (secs + 946684800) * 1000; // 2000-01-01 00:00:00-UTC.
        DateTimeZone dtz = DateTimeZone.getDefault();
        millis -= dtz.getOffset(millis);
        return millis;
    }

}