You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2026/10/01 17:29:49 UTC

(shardingsphere) branch master updated: Throw unsupported exception for OPENQUERY in encrypt feature (#39998)

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

terrymanu 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 6535878aee7 Throw unsupported exception for OPENQUERY in encrypt feature (#39998)
6535878aee7 is described below

commit 6535878aee779c1350bc54da401f7f9a69be7333
Author: Claire <cl...@163.com>
AuthorDate: Fri Oct 2 01:29:42 2026 +0800

    Throw unsupported exception for OPENQUERY in encrypt feature (#39998)
    
    * support checker
    
    * spotless
    
    * update
    
    * support merge statement
    
    * update cts
    
    * update more sql statements
    
    * update delete sql statements
    
    * update
    
    * update
    
    * update
    
    * fix(encrypt): address PR review feedback for EncryptOpenQuerySupportedChecker
    
    Production:
    - Reorder methods to DFS call order (helpers follow callers)
    - Move check() public override before private helpers
    - Fix isOpenQuery to use direct return (eliminate unnecessary local)
    - Add KeyValueSegment handling in expression traversal
    - Add recursive inspection of FunctionTableSegment parameters
    - Unwrap expressions within 200 chars to single lines
    
    Test:
    - Add assertIsCheckWithFunctionTableNestedOpenQuery (OPENJSON nested OPENQUERY)
    - Add assertIsCheckWithKeyValueKeySubqueryOpenQuery (key-side detection)
    - Add assertIsCheckWithOrdinaryKeyValue (negative JSON_OBJECT regression)
    - Rename assertIsCheckWithKeyValueSubqueryOpenQuery to ValueSubquery for clarity
    - Reorder private helpers to first-call order
    
    * fix spotless
---
 .../sql/EncryptSupportedSQLCheckersBuilder.java    |   4 +-
 .../EncryptOpenQuerySupportedChecker.java          | 342 ++++++++++++
 .../EncryptOpenQuerySupportedCheckerTest.java      | 577 +++++++++++++++++++++
 3 files changed, 922 insertions(+), 1 deletion(-)

diff --git a/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/checker/sql/EncryptSupportedSQLCheckersBuilder.java b/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/checker/sql/EncryptSupportedSQLCheckersBuilder.java
index 24cc8851672..dacc687c11e 100644
--- a/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/checker/sql/EncryptSupportedSQLCheckersBuilder.java
+++ b/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/checker/sql/EncryptSupportedSQLCheckersBuilder.java
@@ -19,6 +19,7 @@ package org.apache.shardingsphere.encrypt.checker.sql;
 
 import org.apache.shardingsphere.encrypt.checker.sql.combine.EncryptCombineClauseSupportedChecker;
 import org.apache.shardingsphere.encrypt.checker.sql.insert.EncryptInsertSelectSupportedChecker;
+import org.apache.shardingsphere.encrypt.checker.sql.openquery.EncryptOpenQuerySupportedChecker;
 import org.apache.shardingsphere.encrypt.checker.sql.orderby.EncryptOrderByItemSupportedChecker;
 import org.apache.shardingsphere.encrypt.checker.sql.predicate.EncryptPredicateColumnSupportedChecker;
 import org.apache.shardingsphere.encrypt.checker.sql.projection.EncryptInsertSelectProjectionSupportedChecker;
@@ -45,7 +46,8 @@ public final class EncryptSupportedSQLCheckersBuilder implements SupportedSQLChe
             new EncryptOrderByItemSupportedChecker(),
             new EncryptWithClauseSupportedChecker(),
             new EncryptCombineClauseSupportedChecker(),
-            new EncryptInsertSelectSupportedChecker());
+            new EncryptInsertSelectSupportedChecker(),
+            new EncryptOpenQuerySupportedChecker());
     
     @HighFrequencyInvocation
     @Override
diff --git a/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/checker/sql/openquery/EncryptOpenQuerySupportedChecker.java b/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/checker/sql/openquery/EncryptOpenQuerySupportedChecker.java
new file mode 100644
index 00000000000..13eaa1eaa0a
--- /dev/null
+++ b/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/checker/sql/openquery/EncryptOpenQuerySupportedChecker.java
@@ -0,0 +1,342 @@
+/*
+ * 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.encrypt.checker.sql.openquery;
+
+import org.apache.shardingsphere.encrypt.exception.syntax.UnsupportedEncryptSQLException;
+import org.apache.shardingsphere.encrypt.rule.EncryptRule;
+import org.apache.shardingsphere.infra.annotation.HighFrequencyInvocation;
+import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
+import org.apache.shardingsphere.infra.checker.SupportedSQLChecker;
+import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
+import org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
+import org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereSchema;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.assignment.ColumnAssignmentSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.assignment.InsertValuesSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.BetweenExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.BinaryOperationExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.CaseWhenExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.CollateExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ExistsSubqueryExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ExpressionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.FunctionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.InExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.KeyValueSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ListExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.NotExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.QuantifySubqueryExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.TypeCastExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.complex.CommonTableExpressionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.subquery.SubqueryExpressionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ExpressionProjectionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ProjectionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ProjectionsSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.SubqueryProjectionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.merge.MergeWhenAndThenSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.OrderBySegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.item.ExpressionOrderByItemSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.item.OrderByItemSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.WithSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.CollectionTableSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.DeleteMultiTableSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.FunctionTableSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.JoinTableSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SubqueryTableSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.TableSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
+import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.DeleteStatement;
+import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.InsertStatement;
+import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.MergeStatement;
+import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.SelectStatement;
+import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.UpdateStatement;
+
+import java.util.Collection;
+
+/**
+ * OPENQUERY supported checker for encrypt.
+ */
+@HighFrequencyInvocation
+public final class EncryptOpenQuerySupportedChecker implements SupportedSQLChecker<SQLStatementContext, EncryptRule> {
+    
+    private static final String OPENQUERY_FUNCTION_NAME = "OPENQUERY";
+    
+    private static final String SQLSERVER_DATABASE_TYPE = "SQLServer";
+    
+    @Override
+    public boolean isCheck(final SQLStatementContext sqlStatementContext) {
+        SQLStatement sqlStatement = sqlStatementContext.getSqlStatement();
+        if (!SQLSERVER_DATABASE_TYPE.equals(sqlStatement.getDatabaseType().getType())) {
+            return false;
+        }
+        if (sqlStatement instanceof SelectStatement) {
+            return containsOpenQueryInSelect((SelectStatement) sqlStatement);
+        }
+        if (sqlStatement instanceof UpdateStatement) {
+            return containsOpenQueryInUpdate((UpdateStatement) sqlStatement);
+        }
+        if (sqlStatement instanceof DeleteStatement) {
+            return containsOpenQueryInDelete((DeleteStatement) sqlStatement);
+        }
+        if (sqlStatement instanceof InsertStatement) {
+            return containsOpenQueryInInsert((InsertStatement) sqlStatement);
+        }
+        if (sqlStatement instanceof MergeStatement) {
+            return containsOpenQueryInMerge((MergeStatement) sqlStatement);
+        }
+        return false;
+    }
+    
+    @Override
+    public void check(final EncryptRule rule, final ShardingSphereDatabase database, final ShardingSphereSchema currentSchema, final SQLStatementContext sqlStatementContext) {
+        ShardingSpherePreconditions.checkState(false, () -> new UnsupportedEncryptSQLException("OPENQUERY"));
+    }
+    
+    private boolean containsOpenQueryInSelect(final SelectStatement selectStatement) {
+        return selectStatement.getFrom().map(this::containsOpenQuery).orElse(false)
+                || selectStatement.getWhere().map(optional -> containsOpenQueryInExpression(optional.getExpr())).orElse(false)
+                || selectStatement.getWith().map(this::containsOpenQueryInWith).orElse(false) || containsOpenQueryInSelectClauses(selectStatement);
+    }
+    
+    private boolean containsOpenQuery(final TableSegment tableSegment) {
+        if (tableSegment instanceof FunctionTableSegment) {
+            return isOpenQuery((FunctionTableSegment) tableSegment) || containsOpenQueryInExpression(((FunctionTableSegment) tableSegment).getTableFunction());
+        }
+        if (tableSegment instanceof JoinTableSegment) {
+            return containsOpenQueryInJoin((JoinTableSegment) tableSegment);
+        }
+        if (tableSegment instanceof SubqueryTableSegment) {
+            return containsOpenQueryInSelect(((SubqueryTableSegment) tableSegment).getSubquery().getSelect());
+        }
+        if (tableSegment instanceof DeleteMultiTableSegment) {
+            return containsOpenQuery(((DeleteMultiTableSegment) tableSegment).getRelationTable());
+        }
+        if (tableSegment instanceof CollectionTableSegment) {
+            return containsOpenQueryInExpression(((CollectionTableSegment) tableSegment).getExpressionSegment());
+        }
+        return false;
+    }
+    
+    private boolean isOpenQuery(final FunctionTableSegment functionTableSegment) {
+        return functionTableSegment.getTableFunction() instanceof FunctionSegment && isOpenQueryFunction((FunctionSegment) functionTableSegment.getTableFunction());
+    }
+    
+    private boolean isOpenQueryFunction(final FunctionSegment functionSegment) {
+        return OPENQUERY_FUNCTION_NAME.equalsIgnoreCase(functionSegment.getFunctionName());
+    }
+    
+    private boolean containsOpenQueryInExpression(final ExpressionSegment expression) {
+        if (expression instanceof ExistsSubqueryExpression) {
+            return containsOpenQueryInSelect(((ExistsSubqueryExpression) expression).getSubquery().getSelect());
+        }
+        if (expression instanceof SubqueryExpressionSegment) {
+            return containsOpenQueryInSelect(((SubqueryExpressionSegment) expression).getSubquery().getSelect());
+        }
+        if (expression instanceof QuantifySubqueryExpression) {
+            return containsOpenQueryInSelect(((QuantifySubqueryExpression) expression).getSubquery().getSelect());
+        }
+        return containsOpenQueryInCompoundExpression(expression);
+    }
+    
+    private boolean containsOpenQueryInCompoundExpression(final ExpressionSegment expression) {
+        if (expression instanceof BinaryOperationExpression) {
+            return containsOpenQueryInExpression(((BinaryOperationExpression) expression).getLeft()) || containsOpenQueryInExpression(((BinaryOperationExpression) expression).getRight());
+        }
+        if (expression instanceof InExpression) {
+            return containsOpenQueryInExpression(((InExpression) expression).getLeft()) || containsOpenQueryInExpression(((InExpression) expression).getRight());
+        }
+        if (expression instanceof NotExpression) {
+            return containsOpenQueryInExpression(((NotExpression) expression).getExpression());
+        }
+        if (expression instanceof BetweenExpression) {
+            return containsOpenQueryInExpression(((BetweenExpression) expression).getLeft())
+                    || containsOpenQueryInExpression(((BetweenExpression) expression).getBetweenExpr())
+                    || containsOpenQueryInExpression(((BetweenExpression) expression).getAndExpr());
+        }
+        return containsOpenQueryInRemainingExpression(expression);
+    }
+    
+    private boolean containsOpenQueryInRemainingExpression(final ExpressionSegment expression) {
+        if (expression instanceof ListExpression) {
+            return containsOpenQueryInExpressionList(((ListExpression) expression).getItems());
+        }
+        if (expression instanceof FunctionSegment) {
+            return containsOpenQueryInExpressionList(((FunctionSegment) expression).getParameters());
+        }
+        if (expression instanceof CaseWhenExpression) {
+            return containsOpenQueryInCaseWhen((CaseWhenExpression) expression);
+        }
+        if (expression instanceof TypeCastExpression) {
+            return containsOpenQueryInExpression(((TypeCastExpression) expression).getExpression());
+        }
+        if (expression instanceof CollateExpression) {
+            return ((CollateExpression) expression).getExpr().map(optional -> containsOpenQueryInExpression(optional)).orElse(false);
+        }
+        if (expression instanceof KeyValueSegment) {
+            return containsOpenQueryInKeyValue((KeyValueSegment) expression);
+        }
+        return false;
+    }
+    
+    private boolean containsOpenQueryInExpressionList(final Collection<ExpressionSegment> expressions) {
+        for (ExpressionSegment each : expressions) {
+            if (containsOpenQueryInExpression(each)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    private boolean containsOpenQueryInCaseWhen(final CaseWhenExpression caseWhen) {
+        if (null != caseWhen.getCaseExpr() && containsOpenQueryInExpression(caseWhen.getCaseExpr())) {
+            return true;
+        }
+        if (containsOpenQueryInExpressionList(caseWhen.getWhenExprs()) || containsOpenQueryInExpressionList(caseWhen.getThenExprs())) {
+            return true;
+        }
+        return null != caseWhen.getElseExpr() && containsOpenQueryInExpression(caseWhen.getElseExpr());
+    }
+    
+    private boolean containsOpenQueryInKeyValue(final KeyValueSegment keyValue) {
+        if (null != keyValue.getKey() && containsOpenQueryInExpression(keyValue.getKey())) {
+            return true;
+        }
+        return null != keyValue.getValue() && containsOpenQueryInExpression(keyValue.getValue());
+    }
+    
+    private boolean containsOpenQueryInJoin(final JoinTableSegment joinTableSegment) {
+        if (containsOpenQuery(joinTableSegment.getLeft()) || containsOpenQuery(joinTableSegment.getRight())) {
+            return true;
+        }
+        return null != joinTableSegment.getCondition() && containsOpenQueryInExpression(joinTableSegment.getCondition());
+    }
+    
+    private boolean containsOpenQueryInWith(final WithSegment withSegment) {
+        for (CommonTableExpressionSegment each : withSegment.getCommonTableExpressions()) {
+            if (containsOpenQueryInSelect(each.getSubquery().getSelect())) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    private boolean containsOpenQueryInSelectClauses(final SelectStatement selectStatement) {
+        return selectStatement.getCombine().map(optional -> containsOpenQueryInSelect(optional.getLeft().getSelect()) || containsOpenQueryInSelect(optional.getRight().getSelect())).orElse(false)
+                || containsOpenQueryInProjections(selectStatement.getProjections())
+                || selectStatement.getHaving().map(optional -> containsOpenQueryInExpression(optional.getExpr())).orElse(false)
+                || selectStatement.getOrderBy().map(this::containsOpenQueryInOrderBy).orElse(false);
+    }
+    
+    private boolean containsOpenQueryInProjections(final ProjectionsSegment projectionsSegment) {
+        for (ProjectionSegment each : projectionsSegment.getProjections()) {
+            if (each instanceof SubqueryProjectionSegment && containsOpenQueryInSelect(((SubqueryProjectionSegment) each).getSubquery().getSelect())) {
+                return true;
+            }
+            if (each instanceof ExpressionProjectionSegment && containsOpenQueryInExpression(((ExpressionProjectionSegment) each).getExpr())) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    private boolean containsOpenQueryInOrderBy(final OrderBySegment orderBySegment) {
+        for (OrderByItemSegment each : orderBySegment.getOrderByItems()) {
+            if (each instanceof ExpressionOrderByItemSegment && containsOpenQueryInExpression(((ExpressionOrderByItemSegment) each).getExpr())) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    private boolean containsOpenQueryInUpdate(final UpdateStatement updateStatement) {
+        return containsOpenQuery(updateStatement.getTable()) || updateStatement.getFrom().map(this::containsOpenQuery).orElse(false) || containsOpenQueryInUpdateClauses(updateStatement);
+    }
+    
+    private boolean containsOpenQueryInUpdateClauses(final UpdateStatement updateStatement) {
+        return updateStatement.getWhere().map(optional -> containsOpenQueryInExpression(optional.getExpr())).orElse(false)
+                || updateStatement.getWith().map(this::containsOpenQueryInWith).orElse(false) || containsOpenQueryInAssignments(updateStatement);
+    }
+    
+    private boolean containsOpenQueryInAssignments(final UpdateStatement updateStatement) {
+        return updateStatement.getAssignment().map(optional -> containsOpenQueryInColumnAssignments(optional.getAssignments())).orElse(false);
+    }
+    
+    private boolean containsOpenQueryInColumnAssignments(final Collection<ColumnAssignmentSegment> assignments) {
+        for (ColumnAssignmentSegment each : assignments) {
+            if (containsOpenQueryInExpression(each.getValue())) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    private boolean containsOpenQueryInDelete(final DeleteStatement deleteStatement) {
+        return containsOpenQuery(deleteStatement.getTable())
+                || deleteStatement.getWhere().map(optional -> containsOpenQueryInExpression(optional.getExpr())).orElse(false)
+                || deleteStatement.getWith().map(this::containsOpenQueryInWith).orElse(false);
+    }
+    
+    private boolean containsOpenQueryInInsert(final InsertStatement insertStatement) {
+        if (insertStatement.getRowSetFunction().map(this::isOpenQueryFunction).orElse(false)) {
+            return true;
+        }
+        if (insertStatement.getInsertSelect().map(optional -> containsOpenQueryInSelect(optional.getSelect())).orElse(false)) {
+            return true;
+        }
+        if (insertStatement.getWith().map(this::containsOpenQueryInWith).orElse(false)) {
+            return true;
+        }
+        return containsOpenQueryInInsertValues(insertStatement.getValues());
+    }
+    
+    private boolean containsOpenQueryInInsertValues(final Collection<InsertValuesSegment> valuesSegments) {
+        for (InsertValuesSegment each : valuesSegments) {
+            if (containsOpenQueryInExpressionList(each.getValues())) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    private boolean containsOpenQueryInMerge(final MergeStatement mergeStatement) {
+        return containsOpenQuery(mergeStatement.getTarget()) || containsOpenQuery(mergeStatement.getSource()) || containsOpenQueryInMergeClauses(mergeStatement);
+    }
+    
+    private boolean containsOpenQueryInMergeClauses(final MergeStatement mergeStatement) {
+        if (mergeStatement.getWith().map(this::containsOpenQueryInWith).orElse(false)) {
+            return true;
+        }
+        if (null != mergeStatement.getExpression() && containsOpenQueryInExpression(mergeStatement.getExpression().getExpr())) {
+            return true;
+        }
+        return containsOpenQueryInWhenAndThens(mergeStatement);
+    }
+    
+    private boolean containsOpenQueryInWhenAndThens(final MergeStatement mergeStatement) {
+        for (MergeWhenAndThenSegment each : mergeStatement.getWhenAndThens()) {
+            if (null != each.getAndExpr() && containsOpenQueryInExpression(each.getAndExpr())) {
+                return true;
+            }
+            if (null != each.getUpdate() && containsOpenQueryInUpdate(each.getUpdate())) {
+                return true;
+            }
+            if (null != each.getInsert() && containsOpenQueryInInsert(each.getInsert())) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/features/encrypt/core/src/test/java/org/apache/shardingsphere/encrypt/checker/sql/openquery/EncryptOpenQuerySupportedCheckerTest.java b/features/encrypt/core/src/test/java/org/apache/shardingsphere/encrypt/checker/sql/openquery/EncryptOpenQuerySupportedCheckerTest.java
new file mode 100644
index 00000000000..99c70819b07
--- /dev/null
+++ b/features/encrypt/core/src/test/java/org/apache/shardingsphere/encrypt/checker/sql/openquery/EncryptOpenQuerySupportedCheckerTest.java
@@ -0,0 +1,577 @@
+/*
+ * 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.encrypt.checker.sql.openquery;
+
+import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import org.apache.shardingsphere.encrypt.exception.syntax.UnsupportedEncryptSQLException;
+import org.apache.shardingsphere.encrypt.rewrite.token.generator.fixture.EncryptGeneratorFixtureBuilder;
+import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
+import org.apache.shardingsphere.sql.parser.statement.core.enums.CombineType;
+import org.apache.shardingsphere.sql.parser.statement.core.enums.OrderDirection;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.assignment.ColumnAssignmentSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.assignment.InsertValuesSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.assignment.SetAssignmentSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.column.ColumnSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.combine.CombineSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.BetweenExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.BinaryOperationExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.CaseWhenExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ExistsSubqueryExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ExpressionWithParamsSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.FunctionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.InExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.KeyValueSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.NotExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.QuantifySubqueryExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.complex.CommonTableExpressionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.simple.LiteralExpressionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.subquery.SubqueryExpressionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.subquery.SubquerySegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ExpressionProjectionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ProjectionsSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.SubqueryProjectionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.merge.MergeWhenAndThenSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.OrderBySegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.item.ExpressionOrderByItemSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.predicate.HavingSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.predicate.WhereSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.AliasSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.WithSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.DeleteMultiTableSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.FunctionTableSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.JoinTableSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SimpleTableSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SubqueryTableSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.TableNameSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
+import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.DeleteStatement;
+import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.InsertStatement;
+import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.MergeStatement;
+import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.SelectStatement;
+import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.UpdateStatement;
+import org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+final class EncryptOpenQuerySupportedCheckerTest {
+    
+    @Test
+    void assertIsCheckWithSelectFromOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerStatement(SelectStatement.class);
+        when(selectStatement.getFrom()).thenReturn(Optional.of(createOpenQueryFunctionTableSegment()));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithFunctionTableNestedOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerStatement(SelectStatement.class);
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        FunctionSegment innerFunc = new FunctionSegment(0, 80, "OPENJSON", "OPENJSON((SELECT ...))");
+        innerFunc.getParameters().add(new SubqueryExpressionSegment(subquerySegment));
+        when(selectStatement.getFrom()).thenReturn(Optional.of(new FunctionTableSegment(0, 80, innerFunc)));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithSelectWhereExistsOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        when(selectStatement.getWhere()).thenReturn(Optional.of(new WhereSegment(0, 80, new ExistsSubqueryExpression(0, 80, subquerySegment))));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithSelectCTEOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        stubSelectNegativePaths(selectStatement);
+        WithSegment withSegment = createWithSegmentContainingOpenQuery();
+        when(selectStatement.getWith()).thenReturn(Optional.of(withSegment));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithSelectCombineOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        stubSelectNegativePaths(selectStatement);
+        SubquerySegment rightSubquery = mockSubqueryWithOpenQueryFrom();
+        SubquerySegment leftSubquery = mockSubqueryWithEmptySelect();
+        when(selectStatement.getCombine()).thenReturn(Optional.of(new CombineSegment(0, 80, leftSubquery, CombineType.UNION_ALL, rightSubquery)));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithSelectProjectionSubqueryOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        stubSelectNegativePaths(selectStatement);
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        ProjectionsSegment projections = new ProjectionsSegment(0, 80);
+        projections.getProjections().add(new SubqueryProjectionSegment(subquerySegment, "(SELECT ...)"));
+        when(selectStatement.getProjections()).thenReturn(projections);
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithSelectHavingOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        stubSelectNegativePaths(selectStatement);
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        SubqueryExpressionSegment subqueryExpr = new SubqueryExpressionSegment(subquerySegment);
+        when(selectStatement.getHaving()).thenReturn(Optional.of(new HavingSegment(0, 80,
+                new BinaryOperationExpression(0, 80, new LiteralExpressionSegment(0, 1, 1), subqueryExpr, ">", "1 > (SELECT ...)"))));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithSelectSubqueryTableOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerStatement(SelectStatement.class);
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        when(selectStatement.getFrom()).thenReturn(Optional.of(new SubqueryTableSegment(0, 80, subquerySegment)));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithInSubqueryOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        InExpression inExpr = new InExpression(0, 80, new LiteralExpressionSegment(0, 2, "id"), new SubqueryExpressionSegment(subquerySegment), false);
+        when(selectStatement.getWhere()).thenReturn(Optional.of(new WhereSegment(0, 80, inExpr)));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithBinarySubqueryOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        BinaryOperationExpression binaryExpr = new BinaryOperationExpression(0, 80, new LiteralExpressionSegment(0, 2, "col"),
+                new SubqueryExpressionSegment(subquerySegment), "=", "col = (SELECT ...)");
+        when(selectStatement.getWhere()).thenReturn(Optional.of(new WhereSegment(0, 80, binaryExpr)));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithNotExistsOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        NotExpression notExpr = new NotExpression(0, 80, new ExistsSubqueryExpression(0, 80, subquerySegment), false);
+        when(selectStatement.getWhere()).thenReturn(Optional.of(new WhereSegment(0, 80, notExpr)));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithQuantifySubqueryOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        QuantifySubqueryExpression quantifyExpr = new QuantifySubqueryExpression(0, 80, subquerySegment, "ALL");
+        BinaryOperationExpression binaryExpr = new BinaryOperationExpression(0, 80, new LiteralExpressionSegment(0, 2, "col"), quantifyExpr, ">", "col > ALL (SELECT ...)");
+        when(selectStatement.getWhere()).thenReturn(Optional.of(new WhereSegment(0, 80, binaryExpr)));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithBetweenSubqueryOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        BetweenExpression betweenExpr = new BetweenExpression(0, 80, new LiteralExpressionSegment(0, 2, "id"),
+                new SubqueryExpressionSegment(subquerySegment), new LiteralExpressionSegment(0, 3, 100), false);
+        when(selectStatement.getWhere()).thenReturn(Optional.of(new WhereSegment(0, 80, betweenExpr)));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithBetweenLeftSubqueryOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        BetweenExpression betweenExpr = new BetweenExpression(0, 80, new SubqueryExpressionSegment(subquerySegment),
+                new LiteralExpressionSegment(0, 1, 0), new LiteralExpressionSegment(0, 1, 2), false);
+        when(selectStatement.getWhere()).thenReturn(Optional.of(new WhereSegment(0, 80, betweenExpr)));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithOrderBySubqueryOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        stubSelectNegativePaths(selectStatement);
+        SubqueryExpressionSegment subqueryExpr = new SubqueryExpressionSegment(mockSubqueryWithOpenQueryFrom());
+        ExpressionOrderByItemSegment orderByItem = new ExpressionOrderByItemSegment(0, 80, "(SELECT ...)", OrderDirection.ASC, null, subqueryExpr);
+        when(selectStatement.getOrderBy()).thenReturn(Optional.of(new OrderBySegment(0, 80, Collections.singletonList(orderByItem))));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithCaseWhenSubqueryOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        stubSelectNegativePaths(selectStatement);
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        CaseWhenExpression caseWhenExpr = new CaseWhenExpression(0, 80, null,
+                Collections.singletonList(new ExistsSubqueryExpression(0, 80, subquerySegment)),
+                Collections.singletonList(new LiteralExpressionSegment(0, 1, 1)), new LiteralExpressionSegment(0, 1, 0), "CASE WHEN ...");
+        ProjectionsSegment projections = new ProjectionsSegment(0, 80);
+        projections.getProjections().add(new ExpressionProjectionSegment(0, 80, "CASE WHEN ...", caseWhenExpr));
+        when(selectStatement.getProjections()).thenReturn(projections);
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithFunctionParamSubqueryOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        FunctionSegment funcSeg = new FunctionSegment(0, 80, "ISNULL", "ISNULL((SELECT ...), 0)");
+        funcSeg.getParameters().add(new SubqueryExpressionSegment(subquerySegment));
+        when(selectStatement.getWhere()).thenReturn(Optional.of(new WhereSegment(0, 80,
+                new BinaryOperationExpression(0, 80, funcSeg, new LiteralExpressionSegment(0, 1, 0), ">", "ISNULL(...) > 0"))));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithJoinConditionSubqueryOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerStatement(SelectStatement.class);
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        JoinTableSegment joinTable = new JoinTableSegment();
+        joinTable.setLeft(new SimpleTableSegment(new TableNameSegment(0, 5, new IdentifierValue("foo_t1"))));
+        joinTable.setRight(new SimpleTableSegment(new TableNameSegment(0, 5, new IdentifierValue("foo_t2"))));
+        joinTable.setCondition(new ExistsSubqueryExpression(0, 80, subquerySegment));
+        when(selectStatement.getFrom()).thenReturn(Optional.of(joinTable));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithUpdateTableOpenQuery() {
+        UpdateStatement updateStatement = mockSQLServerStatement(UpdateStatement.class);
+        when(updateStatement.getTable()).thenReturn(createOpenQueryFunctionTableSegment());
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(updateStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithUpdateFromOpenQuery() {
+        UpdateStatement updateStatement = mockSQLServerStatement(UpdateStatement.class);
+        when(updateStatement.getTable()).thenReturn(new SimpleTableSegment(new TableNameSegment(0, 5, new IdentifierValue("foo_table"))));
+        when(updateStatement.getFrom()).thenReturn(Optional.of(createOpenQueryFunctionTableSegment()));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(updateStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithUpdateSetSubqueryOpenQuery() {
+        UpdateStatement updateStatement = mockSQLServerStatement(UpdateStatement.class);
+        when(updateStatement.getTable()).thenReturn(new SimpleTableSegment(new TableNameSegment(0, 5, new IdentifierValue("foo_table"))));
+        when(updateStatement.getFrom()).thenReturn(Optional.empty());
+        when(updateStatement.getWhere()).thenReturn(Optional.empty());
+        when(updateStatement.getWith()).thenReturn(Optional.empty());
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        ColumnSegment column = new ColumnSegment(0, 3, new IdentifierValue("foo_col"));
+        ColumnAssignmentSegment assignment = new ColumnAssignmentSegment(0, 80, Collections.singletonList(column), new SubqueryExpressionSegment(subquerySegment));
+        when(updateStatement.getAssignment()).thenReturn(Optional.of(new SetAssignmentSegment(0, 80, Collections.singletonList(assignment))));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(updateStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithUpdateWhereSubqueryOpenQuery() {
+        UpdateStatement updateStatement = mockSQLServerStatement(UpdateStatement.class);
+        when(updateStatement.getTable()).thenReturn(new SimpleTableSegment(new TableNameSegment(0, 5, new IdentifierValue("foo_table"))));
+        when(updateStatement.getFrom()).thenReturn(Optional.empty());
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        when(updateStatement.getWhere()).thenReturn(Optional.of(new WhereSegment(0, 80, new ExistsSubqueryExpression(0, 80, subquerySegment))));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(updateStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithDeleteTableOpenQuery() {
+        DeleteStatement deleteStatement = mockSQLServerStatement(DeleteStatement.class);
+        when(deleteStatement.getTable()).thenReturn(createOpenQueryFunctionTableSegment());
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(deleteStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithDeleteMultiTableOpenQuery() {
+        DeleteStatement deleteStatement = mockSQLServerStatement(DeleteStatement.class);
+        DeleteMultiTableSegment multiTableSegment = new DeleteMultiTableSegment();
+        JoinTableSegment joinTable = new JoinTableSegment();
+        joinTable.setLeft(new SimpleTableSegment(new TableNameSegment(0, 5, new IdentifierValue("foo_table"))));
+        joinTable.setRight(createOpenQueryFunctionTableSegment());
+        multiTableSegment.setRelationTable(joinTable);
+        when(deleteStatement.getTable()).thenReturn(multiTableSegment);
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(deleteStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithInsertOpenQuery() {
+        InsertStatement insertStatement = mockSQLServerStatement(InsertStatement.class);
+        FunctionSegment funcSeg = new FunctionSegment(0, 60, "OPENQUERY", "OPENQUERY(MyLinkedServer, 'SELECT GroupName FROM Department')");
+        when(insertStatement.getRowSetFunction()).thenReturn(Optional.of(funcSeg));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(insertStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithInsertSelectOpenQuery() {
+        InsertStatement insertStatement = mockSQLServerStatement(InsertStatement.class);
+        when(insertStatement.getRowSetFunction()).thenReturn(Optional.empty());
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        when(insertStatement.getInsertSelect()).thenReturn(Optional.of(subquerySegment));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(insertStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithInsertCTEOpenQuery() {
+        InsertStatement insertStatement = mockSQLServerStatement(InsertStatement.class);
+        when(insertStatement.getRowSetFunction()).thenReturn(Optional.empty());
+        when(insertStatement.getInsertSelect()).thenReturn(Optional.empty());
+        WithSegment withSegment = createWithSegmentContainingOpenQuery();
+        when(insertStatement.getWith()).thenReturn(Optional.of(withSegment));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(insertStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithInsertValuesSubqueryOpenQuery() {
+        InsertStatement insertStatement = mockSQLServerStatement(InsertStatement.class);
+        when(insertStatement.getRowSetFunction()).thenReturn(Optional.empty());
+        when(insertStatement.getInsertSelect()).thenReturn(Optional.empty());
+        when(insertStatement.getWith()).thenReturn(Optional.empty());
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        InsertValuesSegment valuesSegment = new InsertValuesSegment(0, 80, Arrays.asList(new SubqueryExpressionSegment(subquerySegment)));
+        when(insertStatement.getValues()).thenReturn(Collections.singletonList(valuesSegment));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(insertStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithMergeSourceOpenQuery() {
+        MergeStatement mergeStatement = mockSQLServerStatement(MergeStatement.class);
+        when(mergeStatement.getSource()).thenReturn(createOpenQueryFunctionTableSegment());
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(mergeStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithMergeOnConditionOpenQuery() {
+        MergeStatement mergeStatement = mockSQLServerStatement(MergeStatement.class);
+        when(mergeStatement.getTarget()).thenReturn(new SimpleTableSegment(new TableNameSegment(0, 5, new IdentifierValue("foo_table"))));
+        when(mergeStatement.getSource()).thenReturn(new SimpleTableSegment(new TableNameSegment(0, 5, new IdentifierValue("foo_src"))));
+        when(mergeStatement.getWith()).thenReturn(Optional.empty());
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        ExpressionWithParamsSegment onClause = new ExpressionWithParamsSegment(0, 80, new ExistsSubqueryExpression(0, 80, subquerySegment));
+        when(mergeStatement.getExpression()).thenReturn(onClause);
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(mergeStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithMergeWhenConditionOpenQuery() {
+        MergeStatement mergeStatement = mockSQLServerStatement(MergeStatement.class);
+        when(mergeStatement.getTarget()).thenReturn(new SimpleTableSegment(new TableNameSegment(0, 5, new IdentifierValue("foo_table"))));
+        when(mergeStatement.getSource()).thenReturn(new SimpleTableSegment(new TableNameSegment(0, 5, new IdentifierValue("foo_src"))));
+        when(mergeStatement.getWith()).thenReturn(Optional.empty());
+        when(mergeStatement.getExpression()).thenReturn(null);
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        MergeWhenAndThenSegment whenAndThen = new MergeWhenAndThenSegment(0, 80, "WHEN MATCHED AND EXISTS (...) THEN DELETE");
+        whenAndThen.setAndExpr(new ExistsSubqueryExpression(0, 80, subquerySegment));
+        when(mergeStatement.getWhenAndThens()).thenReturn(Collections.singletonList(whenAndThen));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(mergeStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithMergeWhenUpdateSetOpenQuery() {
+        MergeStatement mergeStatement = mockSQLServerStatement(MergeStatement.class);
+        when(mergeStatement.getTarget()).thenReturn(new SimpleTableSegment(new TableNameSegment(0, 5, new IdentifierValue("foo_table"))));
+        when(mergeStatement.getSource()).thenReturn(new SimpleTableSegment(new TableNameSegment(0, 5, new IdentifierValue("foo_src"))));
+        when(mergeStatement.getWith()).thenReturn(Optional.empty());
+        when(mergeStatement.getExpression()).thenReturn(null);
+        UpdateStatement innerUpdate = mock(UpdateStatement.class, RETURNS_DEEP_STUBS);
+        when(innerUpdate.getTable()).thenReturn(new SimpleTableSegment(new TableNameSegment(0, 5, new IdentifierValue("foo_table"))));
+        when(innerUpdate.getFrom()).thenReturn(Optional.empty());
+        when(innerUpdate.getWhere()).thenReturn(Optional.empty());
+        when(innerUpdate.getWith()).thenReturn(Optional.empty());
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        ColumnSegment column = new ColumnSegment(0, 3, new IdentifierValue("foo_col"));
+        ColumnAssignmentSegment assignment = new ColumnAssignmentSegment(0, 80, Collections.singletonList(column), new SubqueryExpressionSegment(subquerySegment));
+        when(innerUpdate.getAssignment()).thenReturn(Optional.of(new SetAssignmentSegment(0, 80, Collections.singletonList(assignment))));
+        MergeWhenAndThenSegment whenAndThen = new MergeWhenAndThenSegment(0, 80, "WHEN MATCHED THEN UPDATE SET ...");
+        whenAndThen.setUpdate(innerUpdate);
+        when(mergeStatement.getWhenAndThens()).thenReturn(Collections.singletonList(whenAndThen));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(mergeStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithMySQL() {
+        assertFalse(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(mockNonSQLServerSelectWithOpenQuery("MySQL"))));
+    }
+    
+    @Test
+    void assertIsCheckWithPostgreSQL() {
+        assertFalse(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(mockNonSQLServerSelectWithOpenQuery("PostgreSQL"))));
+    }
+    
+    @Test
+    void assertIsCheckWithOpenGauss() {
+        assertFalse(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(mockNonSQLServerSelectWithOpenQuery("openGauss"))));
+    }
+    
+    @Test
+    void assertIsCheckWithOracle() {
+        assertFalse(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(mockNonSQLServerSelectWithOpenQuery("Oracle"))));
+    }
+    
+    @Test
+    void assertIsCheckWithNonOpenQueryFunction() {
+        SelectStatement selectStatement = mockSQLServerStatement(SelectStatement.class);
+        FunctionSegment funcSeg = new FunctionSegment(0, 20, "SOME_FUNC", "SOME_FUNC()");
+        when(selectStatement.getFrom()).thenReturn(Optional.of(new FunctionTableSegment(0, 20, funcSeg)));
+        stubSelectNegativePaths(selectStatement);
+        assertFalse(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithSimpleTable() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        stubSelectNegativePaths(selectStatement);
+        assertFalse(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithOpenQueryCaseInsensitive() {
+        SelectStatement selectStatement = mockSQLServerStatement(SelectStatement.class);
+        FunctionSegment funcSeg = new FunctionSegment(0, 50, "openquery", "openquery(Server, 'SELECT 1')");
+        when(selectStatement.getFrom()).thenReturn(Optional.of(new FunctionTableSegment(0, 50, funcSeg)));
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithKeyValueValueSubqueryOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        stubSelectNegativePaths(selectStatement);
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        KeyValueSegment keyValueSegment = new KeyValueSegment(0, 80, "JSON_OBJECT(...)");
+        keyValueSegment.setValue(new SubqueryExpressionSegment(subquerySegment));
+        FunctionSegment funcSeg = new FunctionSegment(0, 80, "JSON_OBJECT", "JSON_OBJECT(...)");
+        funcSeg.getParameters().add(keyValueSegment);
+        ProjectionsSegment projections = new ProjectionsSegment(0, 80);
+        projections.getProjections().add(new ExpressionProjectionSegment(0, 80, "JSON_OBJECT(...)", funcSeg));
+        when(selectStatement.getProjections()).thenReturn(projections);
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithKeyValueKeySubqueryOpenQuery() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        stubSelectNegativePaths(selectStatement);
+        SubquerySegment subquerySegment = mockSubqueryWithOpenQueryFrom();
+        KeyValueSegment keyValueSegment = new KeyValueSegment(0, 80, "JSON_OBJECT(...)");
+        keyValueSegment.setKey(new SubqueryExpressionSegment(subquerySegment));
+        keyValueSegment.setValue(new LiteralExpressionSegment(0, 5, "foo_value"));
+        FunctionSegment funcSeg = new FunctionSegment(0, 80, "JSON_OBJECT", "JSON_OBJECT(...)");
+        funcSeg.getParameters().add(keyValueSegment);
+        ProjectionsSegment projections = new ProjectionsSegment(0, 80);
+        projections.getProjections().add(new ExpressionProjectionSegment(0, 80, "JSON_OBJECT(...)", funcSeg));
+        when(selectStatement.getProjections()).thenReturn(projections);
+        assertTrue(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertIsCheckWithOrdinaryKeyValue() {
+        SelectStatement selectStatement = mockSQLServerSelectWithSimpleFrom();
+        stubSelectNegativePaths(selectStatement);
+        KeyValueSegment keyValueSegment = new KeyValueSegment(0, 80, "JSON_OBJECT(...)");
+        keyValueSegment.setKey(new LiteralExpressionSegment(0, 5, "foo_key"));
+        keyValueSegment.setValue(new LiteralExpressionSegment(0, 5, "foo_value"));
+        FunctionSegment funcSeg = new FunctionSegment(0, 80, "JSON_OBJECT", "JSON_OBJECT(...)");
+        funcSeg.getParameters().add(keyValueSegment);
+        ProjectionsSegment projections = new ProjectionsSegment(0, 80);
+        projections.getProjections().add(new ExpressionProjectionSegment(0, 80, "JSON_OBJECT(...)", funcSeg));
+        when(selectStatement.getProjections()).thenReturn(projections);
+        assertFalse(new EncryptOpenQuerySupportedChecker().isCheck(createSQLStatementContext(selectStatement)));
+    }
+    
+    @Test
+    void assertCheck() {
+        SQLStatementContext sqlStatementContext = mock(SQLStatementContext.class, RETURNS_DEEP_STUBS);
+        assertThrows(UnsupportedEncryptSQLException.class,
+                () -> new EncryptOpenQuerySupportedChecker().check(EncryptGeneratorFixtureBuilder.createEncryptRule(), null, null, sqlStatementContext));
+    }
+    
+    private <T extends SQLStatement> T mockSQLServerStatement(final Class<T> statementClass) {
+        T result = mock(statementClass, RETURNS_DEEP_STUBS);
+        DatabaseType databaseType = mock(DatabaseType.class);
+        when(databaseType.getType()).thenReturn("SQLServer");
+        when(result.getDatabaseType()).thenReturn(databaseType);
+        return result;
+    }
+    
+    private FunctionTableSegment createOpenQueryFunctionTableSegment() {
+        FunctionSegment funcSeg = new FunctionSegment(0, 60, "OPENQUERY", "OPENQUERY(MyLinkedServer, 'SELECT GroupName FROM Department')");
+        return new FunctionTableSegment(0, 60, funcSeg);
+    }
+    
+    private SQLStatementContext createSQLStatementContext(final SQLStatement sqlStatement) {
+        SQLStatementContext result = mock(SQLStatementContext.class);
+        when(result.getSqlStatement()).thenReturn(sqlStatement);
+        return result;
+    }
+    
+    private SubquerySegment mockSubqueryWithOpenQueryFrom() {
+        SelectStatement innerSelect = mock(SelectStatement.class);
+        when(innerSelect.getFrom()).thenReturn(Optional.of(createOpenQueryFunctionTableSegment()));
+        stubSelectNegativePaths(innerSelect);
+        SubquerySegment result = mock(SubquerySegment.class);
+        when(result.getSelect()).thenReturn(innerSelect);
+        return result;
+    }
+    
+    private SelectStatement mockSQLServerSelectWithSimpleFrom() {
+        SelectStatement result = mockSQLServerStatement(SelectStatement.class);
+        when(result.getFrom()).thenReturn(Optional.of(new SimpleTableSegment(new TableNameSegment(0, 5, new IdentifierValue("foo_table")))));
+        return result;
+    }
+    
+    private void stubSelectNegativePaths(final SelectStatement selectStatement) {
+        when(selectStatement.getWhere()).thenReturn(Optional.empty());
+        when(selectStatement.getWith()).thenReturn(Optional.empty());
+        when(selectStatement.getCombine()).thenReturn(Optional.empty());
+        when(selectStatement.getHaving()).thenReturn(Optional.empty());
+        when(selectStatement.getOrderBy()).thenReturn(Optional.empty());
+        when(selectStatement.getProjections()).thenReturn(new ProjectionsSegment(0, 0));
+    }
+    
+    private WithSegment createWithSegmentContainingOpenQuery() {
+        SubquerySegment cteSubquery = mockSubqueryWithOpenQueryFrom();
+        CommonTableExpressionSegment cteSeg = new CommonTableExpressionSegment(0, 80, new AliasSegment(0, 3, new IdentifierValue("foo_cte")), cteSubquery);
+        return new WithSegment(0, 80, Collections.singletonList(cteSeg));
+    }
+    
+    private SubquerySegment mockSubqueryWithEmptySelect() {
+        SelectStatement innerSelect = mock(SelectStatement.class);
+        when(innerSelect.getFrom()).thenReturn(Optional.of(new SimpleTableSegment(new TableNameSegment(0, 5, new IdentifierValue("foo_table")))));
+        stubSelectNegativePaths(innerSelect);
+        SubquerySegment result = mock(SubquerySegment.class);
+        when(result.getSelect()).thenReturn(innerSelect);
+        return result;
+    }
+    
+    private SelectStatement mockNonSQLServerSelectWithOpenQuery(final String databaseTypeName) {
+        SelectStatement result = mock(SelectStatement.class, RETURNS_DEEP_STUBS);
+        DatabaseType databaseType = mock(DatabaseType.class);
+        when(databaseType.getType()).thenReturn(databaseTypeName);
+        when(result.getDatabaseType()).thenReturn(databaseType);
+        when(result.getFrom()).thenReturn(Optional.of(createOpenQueryFunctionTableSegment()));
+        return result;
+    }
+}