diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/MathTrigFunctionEnd2EndIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/MathTrigFunctionEnd2EndIT.java new file mode 100644 index 00000000000..b4f2b4ff509 --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/MathTrigFunctionEnd2EndIT.java @@ -0,0 +1,94 @@ +/* + * 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.assertTrue; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; + +import org.apache.phoenix.expression.function.CosFunction; +import org.apache.phoenix.expression.function.SinFunction; +import org.apache.phoenix.expression.function.TanFunction; +import org.junit.Before; +import org.junit.Test; + +/** + * End to end tests for + * {@link org.apache.phoenix.expression.function.CosFunction} + * {@link org.apache.phoenix.expression.function.SinFunction} + * {@link org.apache.phoenix.expression.function.TanFunction} + */ + +public class MathTrigFunctionEnd2EndIT extends ParallelStatsDisabledIT { + + private static final String KEY = "key"; + private String tableName; + + @Before + public void initTable() throws Exception { + Connection conn = null; + PreparedStatement stmt = null; + tableName = generateUniqueName(); + + try { + conn = DriverManager.getConnection(getUrl()); + String ddl; + ddl = + "CREATE TABLE " + tableName + " (k VARCHAR NOT NULL PRIMARY KEY, doub DOUBLE)"; + conn.createStatement().execute(ddl); + conn.commit(); + } finally { + closeStmtAndConn(stmt, conn); + } + } + + private void updateTableSpec(Connection conn, double data, String tableName) throws Exception { + PreparedStatement stmt = + conn.prepareStatement("UPSERT INTO " + tableName + " VALUES (?, ?)"); + stmt.setString(1, KEY); + stmt.setDouble(2, data); + stmt.executeUpdate(); + conn.commit(); + } + + private void testNumberSpec(Connection conn, double data, String tableName) throws Exception { + updateTableSpec(conn, data, tableName); + ResultSet rs = + conn.createStatement().executeQuery( + "SELECT SIN(doub),COS(doub),TAN(doub) FROM " + tableName); + assertTrue(rs.next()); + Double d = Double.valueOf(data); + assertTrue(twoDoubleEquals(rs.getDouble(1), Math.sin(data))); + assertTrue(twoDoubleEquals(rs.getDouble(2), Math.cos(data))); + assertTrue(twoDoubleEquals(rs.getDouble(3), Math.tan(data))); + + assertTrue(!rs.next()); + } + + @Test + public void test() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + for (double d : new double[] { 0.0, 1.0, -1.0, 123.1234, -123.1234 }) { + testNumberSpec(conn, d, tableName); + } + } +} 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 a18928c4117..8f36e23f533 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 @@ -188,6 +188,9 @@ public enum ExpressionType { ArrayRemoveFunction(ArrayRemoveFunction.class), TransactionProviderNameFunction(TransactionProviderNameFunction.class), MathPIFunction(MathPIFunction.class), + SinFunction(SinFunction.class), + CosFunction(CosFunction.class), + TanFunction(TanFunction.class), ; ExpressionType(Class clazz) { diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CosFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CosFunction.java new file mode 100644 index 00000000000..b6532d8689e --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/CosFunction.java @@ -0,0 +1,56 @@ +/* + * 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.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode.Argument; +import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction; +import org.apache.phoenix.schema.types.PDecimal; +import org.apache.phoenix.schema.types.PDouble; + +@BuiltInFunction(name = CosFunction.NAME, args = { @Argument(allowedTypes = { PDouble.class, + PDecimal.class }) }) +public class CosFunction extends JavaMathOneArgumentFunction { + + public static final String NAME = "COS"; + + public CosFunction() { + } + + public CosFunction(List children) throws SQLException { + super(children); + } + + @Override + public String getName() { + return NAME; + } + + @Override + protected double compute(double firstArg) { + return Math.cos(firstArg); + } + + @Override + public OrderPreserving preservesOrder() { + return OrderPreserving.YES; + } +} diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SinFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SinFunction.java new file mode 100644 index 00000000000..3b29f7f5e37 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/SinFunction.java @@ -0,0 +1,56 @@ +/* + * 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.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode.Argument; +import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction; +import org.apache.phoenix.schema.types.PDecimal; +import org.apache.phoenix.schema.types.PDouble; + +@BuiltInFunction(name = SinFunction.NAME, args = { @Argument(allowedTypes = { PDouble.class, + PDecimal.class }) }) +public class SinFunction extends JavaMathOneArgumentFunction { + + public static final String NAME = "SIN"; + + public SinFunction() { + } + + public SinFunction(List children) throws SQLException { + super(children); + } + + @Override + public String getName() { + return NAME; + } + + @Override + protected double compute(double firstArg) { + return Math.sin(firstArg); + } + + @Override + public OrderPreserving preservesOrder() { + return OrderPreserving.YES; + } +} diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/TanFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/TanFunction.java new file mode 100644 index 00000000000..5951cabc1a6 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/TanFunction.java @@ -0,0 +1,56 @@ +/* + * 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.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode.Argument; +import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction; +import org.apache.phoenix.schema.types.PDecimal; +import org.apache.phoenix.schema.types.PDouble; + +@BuiltInFunction(name = TanFunction.NAME, args = { @Argument(allowedTypes = { PDouble.class, + PDecimal.class }) }) +public class TanFunction extends JavaMathOneArgumentFunction { + + public static final String NAME = "TAN"; + + public TanFunction() { + } + + public TanFunction(List children) throws SQLException { + super(children); + } + + @Override + public String getName() { + return NAME; + } + + @Override + protected double compute(double firstArg) { + return Math.tan(firstArg); + } + + @Override + public OrderPreserving preservesOrder() { + return OrderPreserving.YES; + } +} diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/MathTrigFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/MathTrigFunctionTest.java new file mode 100644 index 00000000000..796106fd21f --- /dev/null +++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/MathTrigFunctionTest.java @@ -0,0 +1,179 @@ +/* + * 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 java.math.BigDecimal; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.expression.function.CosFunction; +import org.apache.phoenix.expression.function.SinFunction; +import org.apache.phoenix.expression.function.TanFunction; +import org.apache.phoenix.query.BaseTest; +import org.apache.phoenix.schema.SortOrder; +import org.apache.phoenix.schema.types.PDecimal; +import org.apache.phoenix.schema.types.PDouble; +import org.apache.phoenix.schema.types.PFloat; +import org.apache.phoenix.schema.types.PInteger; +import org.apache.phoenix.schema.types.PLong; +import org.apache.phoenix.schema.types.PNumericType; +import org.apache.phoenix.schema.types.PSmallint; +import org.apache.phoenix.schema.types.PTinyint; +import org.apache.phoenix.schema.types.PUnsignedDouble; +import org.apache.phoenix.schema.types.PUnsignedFloat; +import org.apache.phoenix.schema.types.PUnsignedInt; +import org.apache.phoenix.schema.types.PUnsignedLong; +import org.junit.Test; + +import com.google.common.collect.Lists; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +/** + * Unit tests for {@link SinFunction} + * Unit tests for {@link CosFunction} + * Unit tests for {@link TanFunction} + */ + +@RunWith(Parameterized.class) +public class MathTrigFunctionTest { + + private Number[] value; + private PNumericType dataType; + + public MathTrigFunctionTest(Number[] value, PNumericType dataType) { + this.value = value; + this.dataType = dataType; + } + + @Parameters(name = "{0} {1}") + public static Collection data() { + return Arrays.asList(new Object[][]{ + { + new BigDecimal[]{BigDecimal.valueOf(1.0), BigDecimal.valueOf(0.0), + BigDecimal.valueOf(-1.0), BigDecimal.valueOf(123.1234), + BigDecimal.valueOf(-123.1234)}, + PDecimal.INSTANCE + }, + { + new Float[]{1.0f, 0.0f, -1.0f, Float.MAX_VALUE, Float.MIN_VALUE, + -Float.MAX_VALUE, -Float.MIN_VALUE, 123.1234f, -123.1234f}, + PFloat.INSTANCE + }, + { + new Float[]{1.0f, 0.0f, Float.MAX_VALUE, Float.MIN_VALUE, 123.1234f}, + PUnsignedFloat.INSTANCE + }, + { + new Double[]{1.0, 0.0, -1.0, Double.MAX_VALUE, Double.MIN_VALUE, + -Double.MAX_VALUE, -Double.MIN_VALUE, 123.1234, -123.1234}, + PDouble.INSTANCE + }, + { + new Double[]{1.0, 0.0, Double.MAX_VALUE, Double.MIN_VALUE, 123.1234}, + PUnsignedDouble.INSTANCE + }, + { + new Long[]{(long) 1, (long) 0, (long) -1, Long.MAX_VALUE, + Long.MIN_VALUE, (long) 123, (long) -123}, + PLong.INSTANCE + }, + { + new Long[]{(long) 1, (long) 0, Long.MAX_VALUE, (long) 123}, + PUnsignedLong.INSTANCE + }, + { + new Integer[]{1, 0, -1, Integer.MAX_VALUE, Integer.MIN_VALUE, 123, -123}, + PInteger.INSTANCE + }, + { + new Integer[]{1, 0, Integer.MAX_VALUE, 123}, + PUnsignedInt.INSTANCE + }, + { + new Short[]{(short) 1, (short) 0, (short) -1, Short.MAX_VALUE, + Short.MIN_VALUE, (short) 123, (short) -123}, + PSmallint.INSTANCE + }, + { + new Short[]{(short) 1, (short) 0, Short.MAX_VALUE, (short) 123}, + PSmallint.INSTANCE + }, + { + new Byte[]{(byte) 1, (byte) 0, (byte) -1, Byte.MAX_VALUE, + Byte.MIN_VALUE, (byte) 123, (byte) -123}, + PTinyint.INSTANCE + }, + { + new Byte[]{(byte) 1, (byte) 0, Byte.MAX_VALUE, (byte) 123}, + PTinyint.INSTANCE + } + }); + } + + private boolean testExpression(LiteralExpression literal, double expectedResult, + String testedFunction) throws SQLException { + List expressions = Lists.newArrayList((Expression) literal); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + Expression mathFunction = null; + + if (testedFunction.equals("SIN")) { + mathFunction = new SinFunction(expressions); + } else if (testedFunction.equals("COS")) { + mathFunction = new CosFunction(expressions); + } else if (testedFunction.equals("TAN")) { + mathFunction = new TanFunction(expressions); + } + + boolean ret = mathFunction.evaluate(null, ptr); + if (ret) { + Double result = + (Double) mathFunction.getDataType().toObject(ptr, mathFunction.getSortOrder()); + assertTrue(BaseTest.twoDoubleEquals(result.doubleValue(), expectedResult)); + } + + return ret; + } + + private void test(Number value, PNumericType dataType, double expectedResult, + String testedFunction) + throws SQLException { + LiteralExpression literal = LiteralExpression.newConstant(value, dataType, SortOrder.ASC); + boolean ret1 = testExpression(literal, expectedResult, testedFunction); + + literal = LiteralExpression.newConstant(value, dataType, SortOrder.DESC); + boolean ret2 = testExpression(literal, expectedResult, testedFunction); + assertEquals(ret1, ret2); + } + + @Test + public void testBatch() + throws SQLException { + for (int i = 0; i < value.length; ++i) { + test(value[i], dataType, Math.sin(value[i].doubleValue()), "SIN"); + test(value[i], dataType, Math.cos(value[i].doubleValue()), "COS"); + test(value[i], dataType, Math.tan(value[i].doubleValue()), "TAN"); + } + } +}