You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by pb...@apache.org on 2019/05/01 07:36:45 UTC

[phoenix] 07/12: PHOENIX-5181 support Math sin/cos/tan functions

This is an automated email from the ASF dual-hosted git repository.

pboado pushed a commit to branch 5.x-cdh6
in repository https://gitbox.apache.org/repos/asf/phoenix.git

commit 2f225e3680977d3b4e0ace578d6ccacb69994718
Author: Xinyi Yan <xy...@salesforce.com>
AuthorDate: Thu Mar 7 18:48:57 2019 +0000

    PHOENIX-5181 support Math sin/cos/tan functions
---
 .../phoenix/end2end/MathTrigFunctionEnd2EndIT.java |  94 +++++++++++
 .../apache/phoenix/expression/ExpressionType.java  |   3 +
 .../phoenix/expression/function/CosFunction.java   |  56 +++++++
 .../phoenix/expression/function/SinFunction.java   |  56 +++++++
 .../phoenix/expression/function/TanFunction.java   |  56 +++++++
 .../phoenix/expression/MathTrigFunctionTest.java   | 179 +++++++++++++++++++++
 6 files changed, 444 insertions(+)

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 0000000..b4f2b4f
--- /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 a18928c..8f36e23 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<? extends Expression> 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 0000000..b6532d8
--- /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<Expression> 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 0000000..3b29f7f
--- /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<Expression> 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 0000000..5951cab
--- /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<Expression> 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 0000000..796106f
--- /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<Object> 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<Expression> 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");
+        }
+    }
+}