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

duanzhengqiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new 8491a00b067 Support for federated query NOT operator (#32961)
8491a00b067 is described below

commit 8491a00b06739c8ab87eec83e25c00c5d61df4c0
Author: niu niu <[email protected]>
AuthorDate: Mon Sep 23 17:43:02 2024 +0800

    Support for federated query NOT operator (#32961)
    
    * Support for federated query NOT operator
    
    * Improve not operator sql node converter
    
    * Format code
---
 .../operator/common/SQLExtensionOperatorTable.java |  5 ++
 .../impl/LiteralExpressionConverter.java           |  3 +
 .../expression/impl/NotExpressionConverter.java    |  3 +-
 .../function/mysql/MySQLOperatorTable.java         | 34 +++++++++++
 .../function/mysql/impl/MySQLNotFunction.java      | 69 ++++++++++++++++++++++
 .../planner/util/SQLFederationPlannerUtils.java    |  3 +-
 .../test/resources/cases/dql/e2e-dql-select.xml    | 16 +++++
 .../src/test/resources/converter/select.xml        |  2 +
 .../parser/src/main/resources/case/dml/select.xml  | 55 +++++++++++++++++
 .../main/resources/sql/supported/dml/select.xml    |  2 +
 10 files changed, 189 insertions(+), 3 deletions(-)

diff --git 
a/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/operator/common/SQLExtensionOperatorTable.java
 
b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/operator/common/SQLExtensionOperatorTable.java
index cfc8d3ced63..110f80291c1 100644
--- 
a/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/operator/common/SQLExtensionOperatorTable.java
+++ 
b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/operator/common/SQLExtensionOperatorTable.java
@@ -22,6 +22,9 @@ import lombok.NoArgsConstructor;
 import org.apache.calcite.sql.SqlBinaryOperator;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlPrefixOperator;
+import org.apache.calcite.sql.type.InferTypes;
+import org.apache.calcite.sql.type.OperandTypes;
+import org.apache.calcite.sql.type.ReturnTypes;
 import 
org.apache.shardingsphere.sqlfederation.optimizer.converter.operator.dialect.mysql.MySQLMatchAgainstOperator;
 
 /**
@@ -60,5 +63,7 @@ public final class SQLExtensionOperatorTable {
     
     public static final SqlPrefixOperator TILDE = new SqlPrefixOperator("~", 
SqlKind.OTHER, 26, null, null, null);
     
+    public static final SqlPrefixOperator NOT = new SqlPrefixOperator("NOT", 
SqlKind.NOT, 26, ReturnTypes.BIGINT, InferTypes.BOOLEAN, OperandTypes.ANY);
+    
     public static final MySQLMatchAgainstOperator MATCH_AGAINST = new 
MySQLMatchAgainstOperator();
 }
diff --git 
a/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/segment/expression/impl/LiteralExpressionConverter.java
 
b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/segment/expression/impl/LiteralExpressionConverter.java
index 63ef34b78a5..ad63003be69 100644
--- 
a/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/segment/expression/impl/LiteralExpressionConverter.java
+++ 
b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/segment/expression/impl/LiteralExpressionConverter.java
@@ -76,6 +76,9 @@ public final class LiteralExpressionConverter {
         if (segment.getLiterals() instanceof String) {
             return Optional.of(SqlLiteral.createCharString(literalValue, 
SqlParserPos.ZERO));
         }
+        if (segment.getLiterals() instanceof Boolean) {
+            return 
Optional.of(SqlLiteral.createBoolean(Boolean.parseBoolean(literalValue), 
SqlParserPos.ZERO));
+        }
         return Optional.empty();
     }
 }
diff --git 
a/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/segment/expression/impl/NotExpressionConverter.java
 
b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/segment/expression/impl/NotExpressionConverter.java
index 6588f4473f8..f63f93c2c78 100644
--- 
a/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/segment/expression/impl/NotExpressionConverter.java
+++ 
b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/segment/expression/impl/NotExpressionConverter.java
@@ -21,7 +21,6 @@ import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 import org.apache.calcite.sql.SqlBasicCall;
 import org.apache.calcite.sql.SqlNode;
-import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.parser.SqlParserPos;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.NotExpression;
 import 
org.apache.shardingsphere.sqlfederation.optimizer.converter.operator.common.SQLExtensionOperatorTable;
@@ -50,6 +49,6 @@ public final class NotExpressionConverter {
         if (segment.getNotSign().equals(true)) {
             return Optional.of(new 
SqlBasicCall(SQLExtensionOperatorTable.NOT_SIGN, sqlNodes, SqlParserPos.ZERO));
         }
-        return Optional.of(new SqlBasicCall(SqlStdOperatorTable.NOT, sqlNodes, 
SqlParserPos.ZERO));
+        return Optional.of(new SqlBasicCall(SQLExtensionOperatorTable.NOT, 
sqlNodes, SqlParserPos.ZERO));
     }
 }
diff --git 
a/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/function/mysql/MySQLOperatorTable.java
 
b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/function/mysql/MySQLOperatorTable.java
new file mode 100644
index 00000000000..558921e0a6d
--- /dev/null
+++ 
b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/function/mysql/MySQLOperatorTable.java
@@ -0,0 +1,34 @@
+/*
+ * 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.shardingsphere.sqlfederation.optimizer.function.mysql;
+
+import org.apache.calcite.sql.util.ReflectiveSqlOperatorTable;
+import org.apache.calcite.sql.validate.SqlUserDefinedFunction;
+import 
org.apache.shardingsphere.sqlfederation.optimizer.function.mysql.impl.MySQLNotFunction;
+
+/**
+ * MySQL operator table.
+ */
+public final class MySQLOperatorTable extends ReflectiveSqlOperatorTable {
+    
+    public static final SqlUserDefinedFunction NOT = new MySQLNotFunction();
+    
+    public MySQLOperatorTable() {
+        init();
+    }
+}
diff --git 
a/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/function/mysql/impl/MySQLNotFunction.java
 
b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/function/mysql/impl/MySQLNotFunction.java
new file mode 100644
index 00000000000..986e78efed3
--- /dev/null
+++ 
b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/function/mysql/impl/MySQLNotFunction.java
@@ -0,0 +1,69 @@
+/*
+ * 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.shardingsphere.sqlfederation.optimizer.function.mysql.impl;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.calcite.schema.impl.ScalarFunctionImpl;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlSyntax;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.InferTypes;
+import org.apache.calcite.sql.type.OperandTypes;
+import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.validate.SqlUserDefinedFunction;
+
+import java.util.Collections;
+
+/**
+ * MySQL not function.
+ */
+public final class MySQLNotFunction extends SqlUserDefinedFunction {
+    
+    public MySQLNotFunction() {
+        super(new SqlIdentifier("NOT", SqlParserPos.ZERO), SqlKind.NOT, 
ReturnTypes.BIGINT_NULLABLE, InferTypes.BOOLEAN,
+                
OperandTypes.operandMetadata(Collections.singletonList(SqlTypeFamily.ANY), 
typeFactory -> ImmutableList.of(typeFactory.createSqlType(SqlTypeName.BIGINT)), 
null, arg -> false),
+                ScalarFunctionImpl.create(MySQLNotFunction.class, "not"));
+    }
+    
+    @Override
+    public SqlSyntax getSyntax() {
+        return SqlSyntax.PREFIX;
+    }
+    
+    /**
+     * not.
+     *
+     * @param value value
+     * @return not operator result
+     */
+    public static Long not(final Object value) {
+        if (null == value) {
+            return null;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).longValue() == 0 ? 1L : 0L;
+        }
+        if (value instanceof Boolean) {
+            return ((Boolean) value) ? 0L : 1L;
+        }
+        return null;
+    }
+}
diff --git 
a/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/planner/util/SQLFederationPlannerUtils.java
 
b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/planner/util/SQLFederationPlannerUtils.java
index 6701c4a729f..b6b94855efb 100644
--- 
a/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/planner/util/SQLFederationPlannerUtils.java
+++ 
b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/planner/util/SQLFederationPlannerUtils.java
@@ -51,6 +51,7 @@ import org.apache.calcite.sql2rel.StandardConvertletTable;
 import 
org.apache.shardingsphere.infra.database.core.spi.DatabaseTypedSPILoader;
 import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
 import org.apache.shardingsphere.parser.rule.SQLParserRule;
+import 
org.apache.shardingsphere.sqlfederation.optimizer.function.mysql.MySQLOperatorTable;
 import 
org.apache.shardingsphere.sqlfederation.optimizer.function.SQLFederationFunctionRegister;
 import 
org.apache.shardingsphere.sqlfederation.optimizer.metadata.view.ShardingSphereViewExpander;
 import 
org.apache.shardingsphere.sqlfederation.optimizer.planner.rule.converter.EnumerableModifyConverterRule;
@@ -232,7 +233,7 @@ public final class SQLFederationPlannerUtils {
     }
     
     private static SqlOperatorTable getSQLOperatorTable(final 
CalciteCatalogReader catalogReader, final DatabaseType databaseType) {
-        return 
SqlOperatorTables.chain(Arrays.asList(SqlLibraryOperatorTableFactory.INSTANCE.getOperatorTable(
+        return SqlOperatorTables.chain(Arrays.asList(new MySQLOperatorTable(), 
SqlLibraryOperatorTableFactory.INSTANCE.getOperatorTable(
                 Arrays.asList(SqlLibrary.STANDARD, 
DATABASE_TYPE_SQL_LIBRARIES.getOrDefault(databaseType.getType(), 
SqlLibrary.MYSQL))), catalogReader));
     }
     
diff --git a/test/e2e/sql/src/test/resources/cases/dql/e2e-dql-select.xml 
b/test/e2e/sql/src/test/resources/cases/dql/e2e-dql-select.xml
index 1d45a8f2189..0f22234124d 100644
--- a/test/e2e/sql/src/test/resources/cases/dql/e2e-dql-select.xml
+++ b/test/e2e/sql/src/test/resources/cases/dql/e2e-dql-select.xml
@@ -386,4 +386,20 @@
     <test-case sql="SELECT BIN(NULL), BIN(''), BIN(' ')" db-types="MySQL" 
scenario-types="db_tbl_sql_federation">
         <assertion expected-data-source-name="read_dataset" />
     </test-case>
+    
+    <test-case sql="SELECT NOT 1, not 0, NOT null" db-types="MySQL" 
scenario-types="db_tbl_sql_federation">
+        <assertion expected-data-source-name="read_dataset" />
+    </test-case>
+    
+    <test-case sql="SELECT NOT order_id FROM t_order" db-types="MySQL" 
scenario-types="db_tbl_sql_federation">
+        <assertion expected-data-source-name="read_dataset" />
+    </test-case>
+    
+    <test-case sql="SELECT NOT (t1.order_id + t2.order_id) FROM t_order t1 
INNER JOIN t_order_item t2 ON t1.order_id = t2.order_id" db-types="MySQL" 
scenario-types="db_tbl_sql_federation">
+        <assertion expected-data-source-name="read_dataset" />
+    </test-case>
+    
+    <test-case sql="SELECT BIN(NOT 1), NOT BIT_COUNT(0)" db-types="MySQL" 
scenario-types="db_tbl_sql_federation">
+        <assertion expected-data-source-name="read_dataset" />
+    </test-case>
 </e2e-test-cases>
diff --git a/test/it/optimizer/src/test/resources/converter/select.xml 
b/test/it/optimizer/src/test/resources/converter/select.xml
index 437688b8969..740ec9aeb51 100644
--- a/test/it/optimizer/src/test/resources/converter/select.xml
+++ b/test/it/optimizer/src/test/resources/converter/select.xml
@@ -35,4 +35,6 @@
     <test-cases sql-case-id="select_with_json_value_return_type" 
expected-sql="SELECT * FROM `t_order` WHERE JSON_VALUE(`items`, '''$.name''' 
'RETURNING' VARCHAR(100)) = 'jack'" db-types="MySQL" />
     <test-cases sql-case-id="select_projection_with_parameter" 
expected-sql="SELECT 1 &quot;id&quot;, ?, &quot;SYSDATE&quot; 
&quot;create_time&quot;, &quot;TRUNC&quot;(&quot;SYSDATE&quot;) 
&quot;create_date&quot;" db-types="Oracle" sql-case-types="PLACEHOLDER" />
     <test-cases sql-case-id="select_projection_with_parameter" 
expected-sql="SELECT 1 &quot;id&quot;, 'OK' &quot;status&quot;, 
&quot;SYSDATE&quot; &quot;create_time&quot;, 
&quot;TRUNC&quot;(&quot;SYSDATE&quot;) &quot;create_date&quot;" 
db-types="Oracle" sql-case-types="LITERAL" />
+    <test-cases sql-case-id="select_with_not_operator_number" 
expected-sql="SELECT NOT 0, NOT 1, NOT 2" db-types="MySQL" />
+    <test-cases sql-case-id="select_with_not_operator_boolean" 
expected-sql="SELECT NOT TRUE, NOT FALSE" db-types="MySQL" />
 </sql-node-converter-test-cases>
diff --git a/test/it/parser/src/main/resources/case/dml/select.xml 
b/test/it/parser/src/main/resources/case/dml/select.xml
index d001ed31d33..31770a3e40f 100644
--- a/test/it/parser/src/main/resources/case/dml/select.xml
+++ b/test/it/parser/src/main/resources/case/dml/select.xml
@@ -9555,4 +9555,59 @@
             </expr>
         </where>
     </select>
+    
+    <select sql-case-id="select_with_not_operator_number">
+        <projections start-index="7" stop-index="25">
+            <expression-projection text="NOT 0" start-index="7" 
stop-index="11">
+                <expr>
+                    <not-expression start-index="7" stop-index="11">
+                        <expr>
+                            <literal-expression value="0" start-index="11" 
stop-index="11" />
+                        </expr>
+                    </not-expression>
+                </expr>
+            </expression-projection>
+            <expression-projection text="NOT 1" start-index="14" 
stop-index="18">
+                <expr>
+                    <not-expression start-index="14" stop-index="18">
+                        <expr>
+                            <literal-expression value="1" start-index="18" 
stop-index="18" />
+                        </expr>
+                    </not-expression>
+                </expr>
+            </expression-projection>
+            <expression-projection text="NOT 2" start-index="21" 
stop-index="25">
+                <expr>
+                    <not-expression start-index="21" stop-index="25">
+                        <expr>
+                            <literal-expression value="2" start-index="25" 
stop-index="25" />
+                        </expr>
+                    </not-expression>
+                </expr>
+            </expression-projection>
+        </projections>
+    </select>
+    
+    <select sql-case-id="select_with_not_operator_boolean">
+        <projections start-index="7" stop-index="25">
+            <expression-projection text="NOT TRUE" start-index="7" 
stop-index="14">
+                <expr>
+                    <not-expression start-index="7" stop-index="14">
+                        <expr>
+                            <literal-expression value="true" start-index="11" 
stop-index="14" />
+                        </expr>
+                    </not-expression>
+                </expr>
+            </expression-projection>
+            <expression-projection text="NOT FALSE" start-index="17" 
stop-index="25">
+                <expr>
+                    <not-expression start-index="17" stop-index="25">
+                        <expr>
+                            <literal-expression value="false" start-index="21" 
stop-index="25" />
+                        </expr>
+                    </not-expression>
+                </expr>
+            </expression-projection>
+        </projections>
+    </select>
 </sql-parser-test-cases>
diff --git a/test/it/parser/src/main/resources/sql/supported/dml/select.xml 
b/test/it/parser/src/main/resources/sql/supported/dml/select.xml
index 9f199e5b391..c391cd8b72a 100644
--- a/test/it/parser/src/main/resources/sql/supported/dml/select.xml
+++ b/test/it/parser/src/main/resources/sql/supported/dml/select.xml
@@ -283,4 +283,6 @@
     <sql-case id="select_with_reserved_word_with_table_ref" value="select 
xxx.condition from xxx" db-types="MySQL,Doris"/>
     <sql-case id="select_with_reserved_word" value="select describe from xxx" 
db-types="MySQL,Doris"/>
     <sql-case id="select_with_nvl_function_and_interval_hour" value="SELECT * 
FROM t_order t WHERE t.CREATE_TIME &lt;= nvl(END_TIME, sysdate) - INTERVAL ? 
HOUR AND t.STATUS = 'FAILURE'" db-types="Oracle"/>
+    <sql-case id="select_with_not_operator_number" value="SELECT NOT 0, NOT 1, 
NOT 2" db-types="MySQL" />
+    <sql-case id="select_with_not_operator_boolean" value="SELECT NOT TRUE, 
NOT FALSE" db-types="MySQL" />
 </sql-cases>

Reply via email to