PHOENIX-2140 Implement get_byte/set_byte/get_bit/set_bit Binary String build-in function (Shuxiong Ye)
Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/caf65ca7 Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/caf65ca7 Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/caf65ca7 Branch: refs/heads/calcite Commit: caf65ca765040709aeb1ea5da1281f1eecae2614 Parents: d41a0fc Author: Samarth <samarth.j...@salesforce.com> Authored: Wed Aug 5 09:42:15 2015 -0700 Committer: Samarth <samarth.j...@salesforce.com> Committed: Wed Aug 5 09:42:15 2015 -0700 ---------------------------------------------------------------------- .../end2end/GetSetByteBitFunctionEnd2EndIT.java | 100 ++++++++++ .../phoenix/expression/ExpressionType.java | 10 +- .../expression/function/GetBitFunction.java | 96 ++++++++++ .../expression/function/GetByteFunction.java | 96 ++++++++++ .../expression/function/SetBitFunction.java | 81 ++++++++ .../expression/function/SetByteFunction.java | 81 ++++++++ .../apache/phoenix/schema/types/PBinary.java | 2 +- .../phoenix/schema/types/PBinaryBase.java | 86 +++++++++ .../apache/phoenix/schema/types/PVarbinary.java | 2 +- .../phoenix/compile/WhereOptimizerTest.java | 33 +++- .../expression/GetSetByteBitFunctionTest.java | 189 +++++++++++++++++++ .../java/org/apache/phoenix/query/BaseTest.java | 8 + .../java/org/apache/phoenix/util/TestUtil.java | 1 + 13 files changed, 780 insertions(+), 5 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf65ca7/phoenix-core/src/it/java/org/apache/phoenix/end2end/GetSetByteBitFunctionEnd2EndIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/GetSetByteBitFunctionEnd2EndIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/GetSetByteBitFunctionEnd2EndIT.java new file mode 100644 index 0000000..a7f7ca3 --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/GetSetByteBitFunctionEnd2EndIT.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.phoenix.end2end; + +import static org.apache.phoenix.util.TestUtil.closeStmtAndConn; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; + +import org.apache.phoenix.expression.function.GetBitFunction; +import org.apache.phoenix.expression.function.GetByteFunction; +import org.apache.phoenix.expression.function.SetBitFunction; +import org.apache.phoenix.expression.function.SetByteFunction; +import org.junit.Before; +import org.junit.Test; + +/** + * End to end tests for {@link GetByteFunction} {@link SetByteFunction} {@link GetBitFunction} + * {@link SetBitFunction} + */ +public class GetSetByteBitFunctionEnd2EndIT extends BaseHBaseManagedTimeIT { + + private static final String KEY = "key"; + + @Before + public void initTable() throws Exception { + Connection conn = null; + PreparedStatement stmt = null; + try { + conn = DriverManager.getConnection(getUrl()); + String ddl; + ddl = "CREATE TABLE ta (k VARCHAR NOT NULL PRIMARY KEY, b BINARY(4), vb VARBINARY)"; + conn.createStatement().execute(ddl); + conn.commit(); + } finally { + closeStmtAndConn(stmt, conn); + } + } + + @Test + public void test() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO ta VALUES (?, ?, ?)"); + stmt.setString(1, KEY); + stmt.setBytes(2, new byte[] { 1, 2, 3, 4 }); + stmt.setBytes(3, new byte[] { 1, 2, 3, 4 }); + stmt.executeUpdate(); + conn.commit(); + ResultSet rs = + conn.createStatement() + .executeQuery( + "SELECT GET_BYTE(vb, 1), GET_BYTE(b, 1) FROM ta WHERE GET_BYTE(vb, 1)=2 and GET_BYTE(b, 1)=2"); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + assertEquals(2, rs.getInt(2)); + assertTrue(!rs.next()); + rs = + conn.createStatement() + .executeQuery( + "SELECT GET_BIT(b, 0),GET_BIT(b, 9) FROM ta WHERE GET_BIT(vb, 0)=1 and GET_BIT(vb, 9)=1"); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + assertEquals(1, rs.getInt(2)); + assertTrue(!rs.next()); + rs = + conn.createStatement().executeQuery( + "SELECT SET_BYTE(vb, 1, 1), SET_BYTE(b, 1, 1) FROM ta"); + assertTrue(rs.next()); + assertArrayEquals(new byte[] { 1, 1, 3, 4 }, rs.getBytes(1)); + assertArrayEquals(new byte[] { 1, 1, 3, 4 }, rs.getBytes(2)); + assertTrue(!rs.next()); + rs = + conn.createStatement().executeQuery( + "SELECT SET_BIT(vb, 8, 1), SET_BIT(b, 8, 1) FROM ta"); + assertTrue(rs.next()); + assertArrayEquals(new byte[] { 1, 3, 3, 4 }, rs.getBytes(1)); + assertArrayEquals(new byte[] { 1, 3, 3, 4 }, rs.getBytes(2)); + assertTrue(!rs.next()); + } +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf65ca7/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java index 9e9cb45..5dbab69 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java @@ -52,6 +52,8 @@ import org.apache.phoenix.expression.function.FirstValueFunction; import org.apache.phoenix.expression.function.FloorDateExpression; import org.apache.phoenix.expression.function.FloorDecimalExpression; import org.apache.phoenix.expression.function.FloorFunction; +import org.apache.phoenix.expression.function.GetBitFunction; +import org.apache.phoenix.expression.function.GetByteFunction; import org.apache.phoenix.expression.function.HourFunction; import org.apache.phoenix.expression.function.IndexStateNameFunction; import org.apache.phoenix.expression.function.InstrFunction; @@ -88,6 +90,8 @@ import org.apache.phoenix.expression.function.SQLIndexTypeFunction; import org.apache.phoenix.expression.function.SQLTableTypeFunction; import org.apache.phoenix.expression.function.SQLViewTypeFunction; import org.apache.phoenix.expression.function.SecondFunction; +import org.apache.phoenix.expression.function.SetBitFunction; +import org.apache.phoenix.expression.function.SetByteFunction; import org.apache.phoenix.expression.function.SignFunction; import org.apache.phoenix.expression.function.SqlTypeNameFunction; import org.apache.phoenix.expression.function.SqrtFunction; @@ -253,7 +257,11 @@ public enum ExpressionType { ArrayConcatFunction(ArrayConcatFunction.class), ArrayFillFunction(ArrayFillFunction.class), ArrayToStringFunction(ArrayToStringFunction.class), - StringToArrayFunction(StringToArrayFunction.class) + StringToArrayFunction(StringToArrayFunction.class), + GetByteFunction(GetByteFunction.class), + SetByteFunction(SetByteFunction.class), + GetBitFunction(GetBitFunction.class), + SetBitFunction(SetBitFunction.class) ; ExpressionType(Class<? extends Expression> clazz) { http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf65ca7/phoenix-core/src/main/java/org/apache/phoenix/expression/function/GetBitFunction.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/GetBitFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/GetBitFunction.java new file mode 100644 index 0000000..f26a53d --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/GetBitFunction.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.phoenix.expression.function; + +import java.sql.SQLException; +import java.util.List; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.expression.Determinism; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode.Argument; +import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction; +import org.apache.phoenix.schema.tuple.Tuple; +import org.apache.phoenix.schema.types.PBinary; +import org.apache.phoenix.schema.types.PBinaryBase; +import org.apache.phoenix.schema.types.PDataType; +import org.apache.phoenix.schema.types.PInteger; +import org.apache.phoenix.schema.types.PVarbinary; + +@BuiltInFunction(name = GetBitFunction.NAME, args = { + @Argument(allowedTypes = { PBinary.class, PVarbinary.class }), + @Argument(allowedTypes = { PInteger.class }) }) +public class GetBitFunction extends PrefixFunction { + + public static final String NAME = âGET_BITâ; + + private Integer offsetPreCompute; + + public GetBitFunction() { + } + + public GetBitFunction(List<Expression> children) throws SQLException { + super(children); + init(); + } + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { + // get offset parameter + int offset; + if (offsetPreCompute == null) { + Expression offsetExpr = children.get(1); + if (!offsetExpr.evaluate(tuple, ptr)) return false; + offset = (Integer) PInteger.INSTANCE.toObject(ptr, offsetExpr.getSortOrder()); + } else offset = offsetPreCompute; + // get binary data parameter + Expression dataExpr = children.get(0); + if (!dataExpr.evaluate(tuple, ptr)) return false; + if (ptr.getLength() == 0) return true; + int len = ptr.getLength() * Byte.SIZE; + offset = (offset % len + len) % len; + // set result + ((PBinaryBase) dataExpr.getDataType()).getBit(ptr, dataExpr.getSortOrder(), offset, ptr); + return true; + } + + @Override + public PDataType getDataType() { + return PInteger.INSTANCE; + } + + private void init() { + Expression offsetExpr = children.get(1); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + if (offsetExpr.isStateless() && offsetExpr.getDeterminism() == Determinism.ALWAYS + && offsetExpr.evaluate(null, ptr)) { + offsetPreCompute = (Integer) PInteger.INSTANCE.toObject(ptr, offsetExpr.getSortOrder()); + } else offsetPreCompute = null; + } + + @Override + public OrderPreserving preservesOrder() { + return (offsetPreCompute != null && offsetPreCompute == 0) ? OrderPreserving.YES_IF_LAST + : OrderPreserving.NO; + } +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf65ca7/phoenix-core/src/main/java/org/apache/phoenix/expression/function/GetByteFunction.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/GetByteFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/GetByteFunction.java new file mode 100644 index 0000000..8e455ae --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/GetByteFunction.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.phoenix.expression.function; + +import java.sql.SQLException; +import java.util.List; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.expression.Determinism; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode.Argument; +import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction; +import org.apache.phoenix.schema.tuple.Tuple; +import org.apache.phoenix.schema.types.PBinary; +import org.apache.phoenix.schema.types.PBinaryBase; +import org.apache.phoenix.schema.types.PDataType; +import org.apache.phoenix.schema.types.PInteger; +import org.apache.phoenix.schema.types.PVarbinary; + +@BuiltInFunction(name = GetByteFunction.NAME, args = { + @Argument(allowedTypes = { PBinary.class, PVarbinary.class }), + @Argument(allowedTypes = { PInteger.class }) }) +public class GetByteFunction extends PrefixFunction { + + public static final String NAME = "GET_BYTE"; + + private Integer offsetPreCompute; + + public GetByteFunction() { + } + + public GetByteFunction(List<Expression> children) throws SQLException { + super(children); + init(); + } + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { + // get offset parameter + int offset; + if (offsetPreCompute == null) { + Expression offsetExpr = children.get(1); + if (!offsetExpr.evaluate(tuple, ptr)) return false; + offset = (Integer) PInteger.INSTANCE.toObject(ptr, offsetExpr.getSortOrder()); + } else offset = offsetPreCompute; + // get binary data parameter + Expression dataExpr = children.get(0); + if (!dataExpr.evaluate(tuple, ptr)) return false; + if (ptr.getLength() == 0) return true; + int len = ptr.getLength(); + offset = (offset % len + len) % len; + // set result + ((PBinaryBase) dataExpr.getDataType()).getByte(ptr, dataExpr.getSortOrder(), offset, ptr); + return true; + } + + @Override + public PDataType getDataType() { + return PInteger.INSTANCE; + } + + private void init() { + Expression offsetExpr = children.get(1); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + if (offsetExpr.isStateless() && offsetExpr.getDeterminism() == Determinism.ALWAYS + && offsetExpr.evaluate(null, ptr)) { + offsetPreCompute = (Integer) PInteger.INSTANCE.toObject(ptr, offsetExpr.getSortOrder()); + } else offsetPreCompute = null; + } + + @Override + public OrderPreserving preservesOrder() { + return (offsetPreCompute != null && offsetPreCompute == 0) ? OrderPreserving.YES_IF_LAST + : OrderPreserving.NO; + } +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf65ca7/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SetBitFunction.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SetBitFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SetBitFunction.java new file mode 100644 index 0000000..3c85c9c --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SetBitFunction.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.phoenix.expression.function; + +import java.sql.SQLException; +import java.util.List; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode.Argument; +import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction; +import org.apache.phoenix.schema.tuple.Tuple; +import org.apache.phoenix.schema.types.PBinary; +import org.apache.phoenix.schema.types.PBinaryBase; +import org.apache.phoenix.schema.types.PDataType; +import org.apache.phoenix.schema.types.PInteger; +import org.apache.phoenix.schema.types.PVarbinary; + +@BuiltInFunction(name = SetBitFunction.NAME, + args = { @Argument(allowedTypes = { PBinary.class, PVarbinary.class }), + @Argument(allowedTypes = { PInteger.class }), + @Argument(allowedTypes = { PInteger.class }) }) +public class SetBitFunction extends ScalarFunction { + + public static final String NAME = âSET_BITâ; + + public SetBitFunction() { + } + + public SetBitFunction(List<Expression> children) throws SQLException { + super(children); + } + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { + // get offset parameter + Expression offsetExpr = children.get(1); + if (!offsetExpr.evaluate(tuple, ptr)) return false; + int offset = (Integer) PInteger.INSTANCE.toObject(ptr, offsetExpr.getSortOrder()); + // get newValue parameter + Expression newValueExpr = children.get(2); + if (!newValueExpr.evaluate(tuple, ptr)) return false; + int newValue = (Integer) PInteger.INSTANCE.toObject(ptr, newValueExpr.getSortOrder()); + byte newByteValue = (byte) (newValue & 0x1); + // get binary data parameter + Expression dataExpr = children.get(0); + if (!dataExpr.evaluate(tuple, ptr)) return false; + if (ptr.getLength() == 0) return true; + int len = ptr.getLength() * Byte.SIZE; + offset = (offset % len + len) % len; + // set result + ((PBinaryBase) dataExpr.getDataType()).setBit(ptr, dataExpr.getSortOrder(), offset, + newByteValue, ptr); + return true; + } + + @Override + public PDataType getDataType() { + return children.get(0).getDataType(); + } +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf65ca7/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SetByteFunction.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SetByteFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SetByteFunction.java new file mode 100644 index 0000000..b32bbcf --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SetByteFunction.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.phoenix.expression.function; + +import java.sql.SQLException; +import java.util.List; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode.Argument; +import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction; +import org.apache.phoenix.schema.tuple.Tuple; +import org.apache.phoenix.schema.types.PBinary; +import org.apache.phoenix.schema.types.PBinaryBase; +import org.apache.phoenix.schema.types.PDataType; +import org.apache.phoenix.schema.types.PInteger; +import org.apache.phoenix.schema.types.PVarbinary; + +@BuiltInFunction(name = SetByteFunction.NAME, + args = { @Argument(allowedTypes = { PBinary.class, PVarbinary.class }), + @Argument(allowedTypes = { PInteger.class }), + @Argument(allowedTypes = { PInteger.class }) }) +public class SetByteFunction extends ScalarFunction { + + public static final String NAME = âSET_BYTEâ; + + public SetByteFunction() { + } + + public SetByteFunction(List<Expression> children) throws SQLException { + super(children); + } + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { + // get offset parameter + Expression offsetExpr = children.get(1); + if (!offsetExpr.evaluate(tuple, ptr)) return false; + int offset = (Integer) PInteger.INSTANCE.toObject(ptr, offsetExpr.getSortOrder()); + // get newValue parameter + Expression newValueExpr = children.get(2); + if (!newValueExpr.evaluate(tuple, ptr)) return false; + int newValue = (Integer) PInteger.INSTANCE.toObject(ptr, newValueExpr.getSortOrder()); + byte newByteValue = (byte) (newValue & 0xff); + // get binary data parameter + Expression dataExpr = children.get(0); + if (!dataExpr.evaluate(tuple, ptr)) return false; + if (ptr.getLength() == 0) return true; + int len = ptr.getLength(); + offset = (offset % len + len) % len; + // set result + ((PBinaryBase) dataExpr.getDataType()).setByte(ptr, dataExpr.getSortOrder(), offset, + newByteValue, ptr); + return true; + } + + @Override + public PDataType getDataType() { + return children.get(0).getDataType(); + } +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf65ca7/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinary.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinary.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinary.java index 8e0c4b5..9aa3f42 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinary.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinary.java @@ -28,7 +28,7 @@ import org.apache.phoenix.exception.DataExceedsCapacityException; import org.apache.phoenix.query.QueryConstants; import org.apache.phoenix.schema.SortOrder; -public class PBinary extends PDataType<byte[]> { +public class PBinary extends PBinaryBase { public static final PBinary INSTANCE = new PBinary(); http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf65ca7/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinaryBase.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinaryBase.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinaryBase.java new file mode 100644 index 0000000..ec0793e --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinaryBase.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.phoenix.schema.types; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.schema.SortOrder; + +public abstract class PBinaryBase extends PDataType<byte[]> { + + protected PBinaryBase(String sqlTypeName, int sqlType, Class clazz, + org.apache.phoenix.schema.types.PDataType.PDataCodec codec, int ordinal) { + super(sqlTypeName, sqlType, clazz, codec, ordinal); + } + + public void getByte(ImmutableBytesWritable ptr, SortOrder sortOrder, int offset, + ImmutableBytesWritable outPtr) { + getByte(ptr.get(), ptr.getOffset(), ptr.getLength(), sortOrder, offset, outPtr); + } + + public void getByte(byte[] bytes, int offset, int length, SortOrder sortOrder, int off, + ImmutableBytesWritable outPtr) { + byte ret = bytes[offset + off]; + if (sortOrder == SortOrder.DESC) ret = SortOrder.invert(ret); + outPtr.set(PInteger.INSTANCE.toBytes(Integer.valueOf(ret))); + } + + public void setByte(ImmutableBytesWritable ptr, SortOrder sortOrder, int offset, byte newValue, + ImmutableBytesWritable outPtr) { + setByte(ptr.get(), ptr.getOffset(), ptr.getLength(), sortOrder, offset, newValue, outPtr); + } + + public void setByte(byte[] bytes, int offset, int length, SortOrder sortOrder, int off, + byte newValue, ImmutableBytesWritable outPtr) { + byte[] ret; + if (sortOrder == SortOrder.ASC) { + ret = new byte[length]; + System.arraycopy(bytes, offset, ret, 0, length); + } else { + ret = SortOrder.invert(bytes, offset, length); + } + ret[off] = newValue; + outPtr.set(ret); + } + + public void getBit(ImmutableBytesWritable ptr, SortOrder sortOrder, int offset, + ImmutableBytesWritable outPtr) { + getBit(ptr.get(), ptr.getOffset(), ptr.getLength(), sortOrder, offset, outPtr); + } + + public void getBit(byte[] bytes, int offset, int length, SortOrder sortOrder, int off, + ImmutableBytesWritable outPtr) { + byte ret = bytes[offset + (off / Byte.SIZE)]; + if (sortOrder == SortOrder.DESC) ret = SortOrder.invert(ret); + ret &= 1 << (off % Byte.SIZE); + ret = (ret != 0) ? (byte) 1 : (byte) 0; + outPtr.set(PInteger.INSTANCE.toBytes(Integer.valueOf(ret))); + } + + public void setBit(ImmutableBytesWritable ptr, SortOrder sortOrder, int offset, byte newValue, + ImmutableBytesWritable outPtr) { + setBit(ptr.get(), ptr.getOffset(), ptr.getLength(), sortOrder, offset, newValue, outPtr); + } + + public void setBit(byte[] bytes, int offset, int length, SortOrder sortOrder, int off, + byte newValue, ImmutableBytesWritable outPtr) { + byte ret = bytes[offset + (off / Byte.SIZE)]; + if (sortOrder == SortOrder.DESC) ret = SortOrder.invert(ret); + ret = (byte) ((ret & (~(1 << (off % Byte.SIZE)))) | (newValue << (off % Byte.SIZE))); + setByte(bytes, offset, length, sortOrder, off / Byte.SIZE, ret, outPtr); + } +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf65ca7/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarbinary.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarbinary.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarbinary.java index bb1d4c6..aafa1c6 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarbinary.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarbinary.java @@ -26,7 +26,7 @@ import org.apache.hadoop.hbase.util.Bytes; import org.apache.phoenix.schema.SortOrder; import org.apache.phoenix.util.ByteUtil; -public class PVarbinary extends PDataType<byte[]> { +public class PVarbinary extends PBinaryBase { public static final PVarbinary INSTANCE = new PVarbinary(); http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf65ca7/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java index c1787ca..227cbd7 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java @@ -17,6 +17,7 @@ */ package org.apache.phoenix.compile; +import static org.apache.phoenix.util.TestUtil.BINARY_NAME; import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; import static org.apache.phoenix.util.TestUtil.assertDegenerate; import static org.apache.phoenix.util.TestUtil.assertEmptyScanKey; @@ -68,8 +69,6 @@ import org.apache.phoenix.util.StringUtil; import org.apache.phoenix.util.TestUtil; import org.junit.Test; - - public class WhereOptimizerTest extends BaseConnectionlessQueryTest { private static StatementContext compileStatement(String query) throws SQLException { @@ -106,6 +105,36 @@ public class WhereOptimizerTest extends BaseConnectionlessQueryTest { } @Test + public void testGetByteBitExpression() throws SQLException { + ensureTableCreated(getUrl(), TestUtil.BINARY_NAME); + int result = 1; + String query = "select * from " + BINARY_NAME + " where GET_BYTE(a_binary, 0)=" + result; + Scan scan = compileStatement(query).getScan(); + + byte[] tmpBytes, tmpBytes2, tmpBytes3; + tmpBytes = PInteger.INSTANCE.toBytes(result); + tmpBytes2 = new byte[16]; + System.arraycopy(tmpBytes, 0, tmpBytes2, 0, tmpBytes.length); + tmpBytes = ByteUtil.nextKey(tmpBytes); + tmpBytes3 = new byte[16]; + System.arraycopy(tmpBytes, 0, tmpBytes3, 0, tmpBytes.length); + assertArrayEquals(tmpBytes2, scan.getStartRow()); + assertArrayEquals(tmpBytes3, scan.getStopRow()); + + query = "select * from " + BINARY_NAME + " where GET_BIT(a_binary, 0)=" + result; + scan = compileStatement(query).getScan(); + + tmpBytes = PInteger.INSTANCE.toBytes(result); + tmpBytes2 = new byte[16]; + System.arraycopy(tmpBytes, 0, tmpBytes2, 0, tmpBytes.length); + tmpBytes = ByteUtil.nextKey(tmpBytes); + tmpBytes3 = new byte[16]; + System.arraycopy(tmpBytes, 0, tmpBytes3, 0, tmpBytes.length); + assertArrayEquals(tmpBytes2, scan.getStartRow()); + assertArrayEquals(tmpBytes3, scan.getStopRow()); + } + + @Test public void testSingleCharPaddedKeyExpression() throws SQLException { String tenantId = "1"; String query = "select * from atable where organization_id='" + tenantId + "'"; http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf65ca7/phoenix-core/src/test/java/org/apache/phoenix/expression/GetSetByteBitFunctionTest.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/GetSetByteBitFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/GetSetByteBitFunctionTest.java new file mode 100644 index 0000000..f01ce3b --- /dev/null +++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/GetSetByteBitFunctionTest.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.phoenix.expression; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import java.sql.SQLException; +import java.util.List; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.expression.function.GetBitFunction; +import org.apache.phoenix.expression.function.GetByteFunction; +import org.apache.phoenix.expression.function.SetBitFunction; +import org.apache.phoenix.expression.function.SetByteFunction; +import org.apache.phoenix.schema.SortOrder; +import org.apache.phoenix.schema.types.PBinary; +import org.apache.phoenix.schema.types.PBinaryBase; +import org.apache.phoenix.schema.types.PInteger; +import org.apache.phoenix.schema.types.PVarbinary; +import org.junit.Test; + +import com.google.common.collect.Lists; + +/** + * Unit tests for {@link GetByteFunction} {@link SetByteFunction} {@link GetBitFunction} + * {@link SetBitFunction} + */ +public class GetSetByteBitFunctionTest { + private void testGetByteExpression(Expression data, Expression offset, int expected) + throws SQLException { + List<Expression> expressions = Lists.newArrayList(data, offset); + Expression getByteFunction = new GetByteFunction(expressions); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + getByteFunction.evaluate(null, ptr); + Integer result = + (Integer) getByteFunction.getDataType().toObject(ptr, + getByteFunction.getSortOrder()); + assertEquals(expected, result.intValue()); + } + + private void testSetByteExpression(Expression data, Expression offset, Expression newValue, + byte[] expected) throws SQLException { + List<Expression> expressions = Lists.newArrayList(data, offset, newValue); + Expression setByteFunction = new SetByteFunction(expressions); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + setByteFunction.evaluate(null, ptr); + byte[] result = + (byte[]) setByteFunction.getDataType() + .toObject(ptr, setByteFunction.getSortOrder()); + assertArrayEquals(expected, result); + } + + private void testGetByte(byte[] bytes, int offset, PBinaryBase dataType, int expected) + throws SQLException { + LiteralExpression dataExpr, offsetExpr; + dataExpr = LiteralExpression.newConstant(bytes, dataType, SortOrder.ASC); + offsetExpr = LiteralExpression.newConstant(offset, PInteger.INSTANCE, SortOrder.ASC); + testGetByteExpression(dataExpr, offsetExpr, expected); + dataExpr = LiteralExpression.newConstant(bytes, dataType, SortOrder.DESC); + offsetExpr = LiteralExpression.newConstant(offset, PInteger.INSTANCE, SortOrder.DESC); + testGetByteExpression(dataExpr, offsetExpr, expected); + } + + private void testSetByte(byte[] bytes, int offset, int newValue, PBinaryBase dataType, + byte[] expected) throws SQLException { + LiteralExpression dataExpr, offsetExpr, newValueExpr; + dataExpr = LiteralExpression.newConstant(bytes, dataType, SortOrder.ASC); + offsetExpr = LiteralExpression.newConstant(offset, PInteger.INSTANCE, SortOrder.ASC); + newValueExpr = LiteralExpression.newConstant(newValue, PInteger.INSTANCE, SortOrder.ASC); + testSetByteExpression(dataExpr, offsetExpr, newValueExpr, expected); + dataExpr = LiteralExpression.newConstant(bytes, dataType, SortOrder.DESC); + offsetExpr = LiteralExpression.newConstant(offset, PInteger.INSTANCE, SortOrder.DESC); + newValueExpr = LiteralExpression.newConstant(newValue, PInteger.INSTANCE, SortOrder.DESC); + testSetByteExpression(dataExpr, offsetExpr, newValueExpr, expected); + } + + @Test + public void testByteBatch() throws SQLException { + byte[] bytes = new byte[256]; + int sum = 0; + for (int i = 0; i < 256; ++i) { + bytes[i] = (byte) (i & 0xff); + sum += bytes[i]; + } + assertEquals(-128, sum); + for (int offset = 0; offset < 256; ++offset) { + testGetByte(bytes, offset, PBinary.INSTANCE, bytes[offset]); + testGetByte(bytes, offset, PVarbinary.INSTANCE, bytes[offset]); + } + for (int offset = 0; offset < 256; ++offset) + for (int tmp = Byte.MIN_VALUE; tmp <= Byte.MAX_VALUE; ++tmp) { + byte[] expected = new byte[bytes.length]; + System.arraycopy(bytes, 0, expected, 0, bytes.length); + expected[offset] = (byte) (tmp & 0xff); + testSetByte(bytes, offset, tmp, PBinary.INSTANCE, expected); + } + } + + private void testGetBitExpression(Expression data, Expression offset, int expected) + throws SQLException { + List<Expression> expressions = Lists.newArrayList(data, offset); + Expression getBitFunction = new GetBitFunction(expressions); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + getBitFunction.evaluate(null, ptr); + Integer result = + (Integer) getBitFunction.getDataType().toObject(ptr, getBitFunction.getSortOrder()); + assertEquals(expected, result.intValue()); + } + + private void testSetBitExpression(Expression data, Expression offset, Expression newValue, + byte[] expected) throws SQLException { + List<Expression> expressions = Lists.newArrayList(data, offset, newValue); + Expression setBitFunction = new SetBitFunction(expressions); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + setBitFunction.evaluate(null, ptr); + byte[] result = + (byte[]) setBitFunction.getDataType().toObject(ptr, setBitFunction.getSortOrder()); + assertArrayEquals(expected, result); + } + + private void testGetBit(byte[] bytes, int offset, PBinaryBase dataType, int expected) + throws SQLException { + LiteralExpression dataExpr, offsetExpr; + dataExpr = LiteralExpression.newConstant(bytes, dataType, SortOrder.ASC); + offsetExpr = LiteralExpression.newConstant(offset, PInteger.INSTANCE, SortOrder.ASC); + testGetBitExpression(dataExpr, offsetExpr, expected); + dataExpr = LiteralExpression.newConstant(bytes, dataType, SortOrder.DESC); + offsetExpr = LiteralExpression.newConstant(offset, PInteger.INSTANCE, SortOrder.DESC); + testGetBitExpression(dataExpr, offsetExpr, expected); + } + + private void testSetBit(byte[] bytes, int offset, int newValue, PBinaryBase dataType, + byte[] expected) throws SQLException { + LiteralExpression dataExpr, offsetExpr, newValueExpr; + dataExpr = LiteralExpression.newConstant(bytes, dataType, SortOrder.ASC); + offsetExpr = LiteralExpression.newConstant(offset, PInteger.INSTANCE, SortOrder.ASC); + newValueExpr = LiteralExpression.newConstant(newValue, PInteger.INSTANCE, SortOrder.ASC); + testSetBitExpression(dataExpr, offsetExpr, newValueExpr, expected); + dataExpr = LiteralExpression.newConstant(bytes, dataType, SortOrder.DESC); + offsetExpr = LiteralExpression.newConstant(offset, PInteger.INSTANCE, SortOrder.DESC); + newValueExpr = LiteralExpression.newConstant(newValue, PInteger.INSTANCE, SortOrder.DESC); + testSetBitExpression(dataExpr, offsetExpr, newValueExpr, expected); + } + + @Test + public void testGetBitBatch() throws SQLException { + byte[] bytes = new byte[256]; + int sum = 0; + for (int i = 0; i < 256; ++i) { + bytes[i] = (byte) (i & 0xff); + sum += bytes[i]; + } + assertEquals(-128, sum); + for (int offset = 0; offset < 256 * Byte.SIZE; ++offset) { + byte expected = + (bytes[offset / Byte.SIZE] & (1 << (offset % Byte.SIZE))) != 0 ? (byte) 1 + : (byte) 0; + testGetBit(bytes, offset, PBinary.INSTANCE, expected); + testGetBit(bytes, offset, PVarbinary.INSTANCE, expected); + } + for (int offset = 0; offset < 256 * Byte.SIZE; ++offset) + for (int tmp = 0; tmp <= 1; ++tmp) { + byte[] expected = new byte[bytes.length]; + System.arraycopy(bytes, 0, expected, 0, bytes.length); + if (tmp != 0) { + expected[offset / Byte.SIZE] |= (byte) (1 << (offset % Byte.SIZE)); + } else { + expected[offset / Byte.SIZE] &= (byte) (~(1 << (offset % Byte.SIZE))); + } + testSetBit(bytes, offset, tmp, PBinary.INSTANCE, expected); + } + } +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf65ca7/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java b/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java index 96b8ea2..5265afc 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java @@ -23,6 +23,7 @@ import static org.apache.phoenix.util.PhoenixRuntime.JDBC_PROTOCOL_TERMINATOR; import static org.apache.phoenix.util.PhoenixRuntime.PHOENIX_TEST_DRIVER_URL_PARAM; import static org.apache.phoenix.util.TestUtil.ATABLE_NAME; import static org.apache.phoenix.util.TestUtil.A_VALUE; +import static org.apache.phoenix.util.TestUtil.BINARY_NAME; import static org.apache.phoenix.util.TestUtil.BTABLE_NAME; import static org.apache.phoenix.util.TestUtil.B_VALUE; import static org.apache.phoenix.util.TestUtil.CUSTOM_ENTITY_DATA_FULL_NAME; @@ -458,6 +459,13 @@ public abstract class BaseTest { " co_item_name varchar " + " CONSTRAINT pk PRIMARY KEY (item_id, item_name)) " + " SALT_BUCKETS=4"); + builder.put(BINARY_NAME,"create table " + BINARY_NAME + + " (a_binary BINARY(16) not null, \n" + + " b_binary BINARY(16), \n" + + " a_varbinary VARBINARY, \n" + + " b_varbinary VARBINARY, \n" + + " CONSTRAINT pk PRIMARY KEY (a_binary)\n" + + ") "); tableDDLMap = builder.build(); } http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf65ca7/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java b/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java index 15e3583..1d56dba 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java @@ -207,6 +207,7 @@ public class TestUtil { public static final String JOIN_ITEM_TABLE_DISPLAY_NAME = JOIN_SCHEMA + "." + JOIN_ITEM_TABLE; public static final String JOIN_SUPPLIER_TABLE_DISPLAY_NAME = JOIN_SCHEMA + "." + JOIN_SUPPLIER_TABLE; public static final String JOIN_COITEM_TABLE_DISPLAY_NAME = JOIN_SCHEMA + "." + JOIN_COITEM_TABLE; + public static final String BINARY_NAME = "BinaryTable"; /** * Read-only properties used by all tests