@Override
    public boolean isFullyCompatible(Type other) {
        if (!other.isScalarType()) {
            return false;
        }

        if (this.equals(other)) {
            return true;
        }

        ScalarType t = (ScalarType) other;
        if (isBoolean()) {
            return t.isIntegerType() || t.isLargeIntType() || t.isStringType() || t.isNumericType();
        }
        if (isIntegerType() || isLargeIntType()) {
            if (t.isIntegerType() || t.isLargeIntType()) {
                return ordinal() < t.ordinal();
            }
            if (t.isStringType()) {
                return true;
            }
            return false;
        }
        if (isDecimalV3()) {
            if (t.isDecimalV3()) {
                return t.precision >= precision && t.scale >= scale;
            }
            if (t.isStringType() || t.isFloatingPointType()) {
                return true;
            }
            return false;
        }
        // both string
        if (isStringType() && t.isStringType()) {
            return true;
        }
        return false;
    }
@Test
    public void testIsFullyCompatible() {
        List<ScalarType> integerTypes = Lists.newArrayList(
                ScalarType.TINYINT,
                ScalarType.SMALLINT,
                ScalarType.INT,
                ScalarType.BIGINT,
                ScalarType.LARGEINT
        );
        List<ScalarType> stringTypes = Lists.newArrayList(
                ScalarType.createCharType(-1),
                ScalarType.createVarcharType(-1)
        );
        List<ScalarType> decimalTypes = Lists.newArrayList(
                ScalarType.createDecimalV3Type(PrimitiveType.DECIMAL32, 3, 0),
                ScalarType.createDecimalV3Type(PrimitiveType.DECIMAL32, 6, 2),
                ScalarType.createDecimalV3Type(PrimitiveType.DECIMAL32, 9, 4),
                ScalarType.createDecimalV3Type(PrimitiveType.DECIMAL64, 12, 8),
                ScalarType.createDecimalV3Type(PrimitiveType.DECIMAL64, 15, 10),
                ScalarType.createDecimalV3Type(PrimitiveType.DECIMAL64, 18, 12),
                ScalarType.createDecimalV3Type(PrimitiveType.DECIMAL128, 21, 14),
                ScalarType.createDecimalV3Type(PrimitiveType.DECIMAL128, 24, 16)
        );

        // integer to integer
        for (int i = 0; i < integerTypes.size(); i++) {
            for (int j = 0; j < integerTypes.size(); j++) {
                Assert.assertEquals(i <= j, integerTypes.get(i).isFullyCompatible(integerTypes.get(j)));
            }
        }
        // integer to string
        for (int i = 0; i < integerTypes.size(); i++) {
            for (int j = 0; j < stringTypes.size(); j++) {
                Assert.assertTrue(integerTypes.get(i).isFullyCompatible(stringTypes.get(j)));
            }
        }
        // decimal to decimal
        for (int i = 0; i < decimalTypes.size(); i++) {
            for (int j = 0; j < decimalTypes.size(); j++) {
                Assert.assertEquals(i <= j, decimalTypes.get(i).isFullyCompatible(decimalTypes.get(j)));
            }
        }
        // decimal to float
        for (int i = 0; i < decimalTypes.size(); i++) {
            Assert.assertTrue(decimalTypes.get(i).isFullyCompatible(ScalarType.FLOAT));
            Assert.assertTrue(decimalTypes.get(i).isFullyCompatible(ScalarType.DOUBLE));
        }
        // decimal to string
        for (int i = 0; i < decimalTypes.size(); i++) {
            for (int j = 0; j < stringTypes.size(); j++) {
                Assert.assertTrue(decimalTypes.get(i).isFullyCompatible(stringTypes.get(j)));
            }
        }
        // string to string
        for (int i = 0; i < stringTypes.size(); i++) {
            for (int j = 0; j < stringTypes.size(); j++) {
                Assert.assertTrue(stringTypes.get(i).isFullyCompatible(stringTypes.get(j)));
            }
        }

        // complex types
        Assert.assertFalse(ScalarType.JSON.isFullyCompatible(ScalarType.INT));
        Assert.assertFalse(ScalarType.JSON.isFullyCompatible(ScalarType.VARCHAR));
    }