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

morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 5e6f1b8  [Feature] Support sql block rule (#6192)
5e6f1b8 is described below

commit 5e6f1b89da8abe27327833243242ed94555a5349
Author: Stalary <[email protected]>
AuthorDate: Fri Aug 13 21:56:34 2021 +0800

    [Feature] Support sql block rule (#6192)
    
    Support grammar:
    - SHOW SQL_BLOCK_RULE [FOR NAME]
    - CREATE SQL_BLOCK_RULE test_rule PROPERTIES ("user"="default", 
"sql"="select .* from .* join .*", "enable": "true");
    - ALTER SQL_BLOCK_RULE test_rule PROPERTIES ("user"="test_user", "enable": 
"false");
    - DROP SQL_BLOCK_RULE test_rule1,test_rule2;
---
 docs/.vuepress/sidebar/en.js                       |   8 +
 docs/.vuepress/sidebar/zh-CN.js                    |   8 +
 .../en/administrator-guide/block-rule/sql-block.md |  59 ++++++
 .../administrator-guide/block-rule/sql-block.md    |  59 ++++++
 fe/fe-core/SchemaChangeV2Test                      | Bin 0 -> 800 bytes
 fe/fe-core/src/main/cup/sql_parser.cup             |  24 ++-
 .../doris/analysis/AlterSqlBlockRuleStmt.java      | 104 +++++++++++
 .../doris/analysis/CreateSqlBlockRuleStmt.java     | 152 +++++++++++++++
 .../doris/analysis/DropSqlBlockRuleStmt.java       |  58 ++++++
 .../doris/analysis/ShowSqlBlockRuleStmt.java       |  83 +++++++++
 .../org/apache/doris/blockrule/SqlBlockRule.java   | 146 +++++++++++++++
 .../apache/doris/blockrule/SqlBlockRuleMgr.java    | 205 +++++++++++++++++++++
 .../java/org/apache/doris/catalog/Catalog.java     |  20 ++
 .../org/apache/doris/common/FeMetaVersion.java     |   4 +-
 .../java/org/apache/doris/common/MetaReader.java   |   1 +
 .../java/org/apache/doris/common/MetaWriter.java   |   2 +
 .../org/apache/doris/journal/JournalEntity.java    |  17 ++
 .../java/org/apache/doris/metric/MetricRepo.java   |   4 +
 .../mysql/privilege/CommonUserProperties.java      |  26 ++-
 .../org/apache/doris/mysql/privilege/PaloAuth.java |  15 +-
 .../apache/doris/mysql/privilege/UserProperty.java |  23 ++-
 .../doris/mysql/privilege/UserPropertyMgr.java     |   8 +
 .../DropSqlBlockRuleOperationLog.java}             |  42 ++---
 .../java/org/apache/doris/persist/EditLog.java     |  28 +++
 .../org/apache/doris/persist/OperationType.java    |   5 +
 .../java/org/apache/doris/plugin/AuditEvent.java   |   7 +
 .../java/org/apache/doris/qe/ConnectContext.java   |  10 +
 .../java/org/apache/doris/qe/ConnectProcessor.java |  15 +-
 .../main/java/org/apache/doris/qe/DdlExecutor.java |   9 +
 .../java/org/apache/doris/qe/ShowExecutor.java     |  14 +-
 fe/fe-core/src/main/jflex/sql_scanner.flex         |   1 +
 .../doris/analysis/AlterSqlBlockRuleStmtTest.java  |  72 ++++++++
 .../doris/analysis/CreateSqlBlockRuleStmtTest.java |  73 ++++++++
 .../doris/blockrule/SqlBlockRuleMgrTest.java       | 129 +++++++++++++
 .../org/apache/doris/catalog/UserPropertyTest.java |  10 +
 .../java/org/apache/doris/qe/ShowExecutorTest.java |  14 ++
 36 files changed, 1415 insertions(+), 40 deletions(-)

diff --git a/docs/.vuepress/sidebar/en.js b/docs/.vuepress/sidebar/en.js
index 96090cf..cf8012e 100644
--- a/docs/.vuepress/sidebar/en.js
+++ b/docs/.vuepress/sidebar/en.js
@@ -182,6 +182,14 @@ module.exports = [
         ],
         sidebarDepth: 1,
       },
+      {
+        title: "Block Rule",
+        directoryPath: "block-rule/",
+        children: [
+          "sql-block",
+        ],
+        sidebarDepth: 1,
+      },
       "backup-restore",
       "broker",
       "colocation-join",
diff --git a/docs/.vuepress/sidebar/zh-CN.js b/docs/.vuepress/sidebar/zh-CN.js
index f2fbb5f..c4fd9b7 100644
--- a/docs/.vuepress/sidebar/zh-CN.js
+++ b/docs/.vuepress/sidebar/zh-CN.js
@@ -181,6 +181,14 @@ module.exports = [
         ],
         sidebarDepth: 1,
       },
+      {
+        title: "拦截规则",
+        directoryPath: "block-rule/",
+        children: [
+          "sql-block",
+        ],
+        sidebarDepth: 1,
+      },
       "backup-restore",
       "broker",
       "colocation-join",
diff --git a/docs/en/administrator-guide/block-rule/sql-block.md 
b/docs/en/administrator-guide/block-rule/sql-block.md
new file mode 100644
index 0000000..ddbd1e5
--- /dev/null
+++ b/docs/en/administrator-guide/block-rule/sql-block.md
@@ -0,0 +1,59 @@
+---
+{
+"title": "SQL Block Rule",
+"language": "en"
+}
+---
+
+<!-- 
+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.
+-->
+
+# SQL Block Rule
+
+Support SQL block rule by user level, by regex way to deny specify SQL
+
+## Rule
+
+SQL block rule CRUD
+- create SQL block rule
+    - sql:Regex pattern,Special characters need to be translated
+    - sqlHash: Sql hash value, Used to match exactly, We print it in 
fe.audit.log
+    - global: Whether global(all users)is in effect, false by default
+    - enable:Whether to enable block rule,true by default
+```
+CREATE SQL_BLOCK_RULE test_rule PROPERTIES("sql"="select \\* from 
test_table","sqlHash":null,"global"="false","enable"="true")
+```
+- show configured SQL block rules, or show all rules if you do not specify a 
rule name
+```
+SHOW SQL_BLOCK_RULE [FOR RULE_NAME]
+```
+- alter SQL block rule,Allows changes sql/global/enable anyone
+```
+ALTER SQL_BLOCK_RULE test_rule PROPERTIES("sql"="select \\* from 
test_table","enable"="true")
+```
+- drop SQL block rule,Support multiple rules, separated by `,`
+```
+DROP SQL_BLOCK_RULE test_rule1,test_rule2
+```
+
+## User bind rules
+If global=false is configured, the rules binding for the specified user needs 
to be configured, with multiple rules separated by ', '
+```
+SET PROPERTY [FOR 'jack'] 'sql_block_rules' = 'test_rule1,test_rule2'
+```
diff --git a/docs/zh-CN/administrator-guide/block-rule/sql-block.md 
b/docs/zh-CN/administrator-guide/block-rule/sql-block.md
new file mode 100644
index 0000000..bfaa167
--- /dev/null
+++ b/docs/zh-CN/administrator-guide/block-rule/sql-block.md
@@ -0,0 +1,59 @@
+---
+{
+"title": "SQL黑名单",
+"language": "zh-CN"
+}
+---
+
+<!-- 
+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.
+-->
+
+# SQL黑名单
+
+支持按用户配置SQL黑名单,通过正则匹配的方式拒绝指定SQL
+
+## 规则
+
+对SQL规则增删改查
+- 创建SQL阻止规则
+    - sql:匹配规则(基于正则匹配,特殊字符需要转译),可选
+    - sqlHash: sql hash值,用于完全匹配,我们会在`fe.audit.log`打印这个值,可选
+    - global:是否全局(所有用户)生效,默认为false  
+    - enable:是否开启阻止规则,默认为true
+```
+CREATE SQL_BLOCK_RULE test_rule PROPERTIES("sql"="select \\* from 
test_table","sqlHash":null,"enable"="true")
+```
+- 查看已配置的SQL阻止规则,不指定规则名则为查看所有规则
+```
+SHOW SQL_BLOCK_RULE [FOR RULE_NAME]
+```
+- 修改SQL阻止规则,允许对sql/global/enable等每一项进行修改
+```
+ALTER SQL_BLOCK_RULE test_rule PROPERTIES("sql"="select \\* from 
test_table","enable"="true")
+```
+- 删除SQL阻止规则,支持多规则,以`,`隔开
+```
+DROP SQL_BLOCK_RULE test_rule1,test_rule2
+```
+
+## 用户规则绑定
+如果配置global=false,则需要配置指定用户的规则绑定,多个规则使用`,`分隔
+```
+SET PROPERTY [FOR 'jack'] 'sql_block_rules' = 'test_rule1,test_rule2'
+```
\ No newline at end of file
diff --git a/fe/fe-core/SchemaChangeV2Test b/fe/fe-core/SchemaChangeV2Test
new file mode 100644
index 0000000..0a76f63
Binary files /dev/null and b/fe/fe-core/SchemaChangeV2Test differ
diff --git a/fe/fe-core/src/main/cup/sql_parser.cup 
b/fe/fe-core/src/main/cup/sql_parser.cup
index c360419..232d9fb 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -270,7 +270,7 @@ terminal String KW_ADD, KW_ADMIN, KW_AFTER, KW_AGGREGATE, 
KW_ALIAS, KW_ALL, KW_A
     KW_UNCOMMITTED, KW_UNBOUNDED, KW_UNION, KW_UNIQUE, KW_UNSIGNED, KW_USE, 
KW_USER, KW_USING, KW_UNINSTALL,
     KW_VALUE, KW_VALUES, KW_VARCHAR, KW_VARIABLES, KW_VERBOSE, KW_VIEW,
     KW_WARNINGS, KW_WEEK, KW_WHEN, KW_WHITELIST, KW_WHERE, KW_WITH, KW_WORK, 
KW_WRITE,
-    KW_YEAR;
+    KW_YEAR, KW_SQL_BLOCK_RULE;
 
 terminal COMMA, COLON, DOT, DOTDOTDOT, AT, STAR, LPAREN, RPAREN, SEMICOLON, 
LBRACKET, RBRACKET, DIVIDE, MOD, ADD, SUBTRACT;
 terminal BITAND, BITOR, BITXOR, BITNOT;
@@ -827,6 +827,11 @@ alter_stmt ::=
     {:
         RESULT = new AlterRoutineLoadStmt(jobLabel, jobProperties, 
datasourceProperties);
     :}
+    | KW_ALTER KW_SQL_BLOCK_RULE ident:ruleName
+    opt_properties:properties
+    {:
+        RESULT = new AlterSqlBlockRuleStmt(ruleName, properties);
+    :}
     ;
 
 opt_datasource_properties ::=
@@ -1254,6 +1259,11 @@ create_stmt ::=
     {:
         RESULT = new CreateDataSyncJobStmt(jobName, db, channelDescList, 
binlog, properties);
     :}
+    /* sql_block_rule */
+    | KW_CREATE KW_SQL_BLOCK_RULE ident:ruleName opt_properties:properties
+    {:
+        RESULT = new CreateSqlBlockRuleStmt(ruleName, properties);
+    :}
     ;
 
 channel_desc_list ::=
@@ -1955,6 +1965,10 @@ drop_stmt ::=
     {:
         RESULT = new DropEncryptKeyStmt(keyName);
     :}
+    | KW_DROP KW_SQL_BLOCK_RULE ident_list:ruleNames
+    {:
+        RESULT = new DropSqlBlockRuleStmt(ruleNames);
+    :}
     ;
 
 // Recover statement
@@ -2422,6 +2436,14 @@ show_stmt ::=
     {:
         RESULT = stmt;
     :}
+    | KW_SHOW KW_SQL_BLOCK_RULE KW_FOR ident:ruleName
+    {:
+        RESULT = new ShowSqlBlockRuleStmt(ruleName);
+    :}
+    | KW_SHOW KW_SQL_BLOCK_RULE
+    {:
+        RESULT = new ShowSqlBlockRuleStmt(null);
+    :}
     ;
 
 show_param ::=
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterSqlBlockRuleStmt.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterSqlBlockRuleStmt.java
new file mode 100644
index 0000000..6b7dfa0
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterSqlBlockRuleStmt.java
@@ -0,0 +1,104 @@
+// 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.doris.analysis;
+
+import org.apache.doris.catalog.Catalog;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.UserException;
+import org.apache.doris.common.util.PrintableMap;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.qe.ConnectContext;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Map;
+
+public class AlterSqlBlockRuleStmt extends DdlStmt {
+
+    private final String ruleName;
+
+    private String sql;
+
+    private String sqlHash;
+
+    private Boolean global;
+
+    private Boolean enable;
+
+    private final Map<String, String> properties;
+
+    public AlterSqlBlockRuleStmt(String ruleName, Map<String, String> 
properties) {
+        this.ruleName = ruleName;
+        this.properties = properties;
+    }
+
+    @Override
+    public void analyze(Analyzer analyzer) throws UserException {
+        super.analyze(analyzer);
+        // check auth
+        if 
(!Catalog.getCurrentCatalog().getAuth().checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.ADMIN)) {
+            
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"ADMIN");
+        }
+        // check properties
+        CreateSqlBlockRuleStmt.checkCommonProperties(properties);
+        setProperties(properties);
+    }
+
+    private void setProperties(Map<String, String> properties) {
+        this.sql = properties.get(CreateSqlBlockRuleStmt.SQL_PROPERTY);
+        this.sqlHash = 
properties.get(CreateSqlBlockRuleStmt.SQL_HASH_PROPERTY);
+        // allow null, represents no modification
+        String globalStr = 
properties.get(CreateSqlBlockRuleStmt.GLOBAL_PROPERTY);
+        this.global = StringUtils.isNotEmpty(globalStr) ? 
Boolean.parseBoolean(globalStr) : null;
+        String enableStr = 
properties.get(CreateSqlBlockRuleStmt.ENABLE_PROPERTY);
+        this.enable = StringUtils.isNotEmpty(enableStr) ? 
Boolean.parseBoolean(enableStr) : null;
+    }
+
+    public String getRuleName() {
+        return ruleName;
+    }
+
+    public String getSql() {
+        return sql;
+    }
+
+    public Boolean getGlobal() {
+        return global;
+    }
+
+    public Boolean getEnable() {
+        return enable;
+    }
+
+    public String getSqlHash() {
+        return sqlHash;
+    }
+
+    @Override
+    public String toSql() {
+        // ALTER SQL_BLOCK_RULE test_rule PROPERTIES("sql"="select \\* from 
test_table","enable"="true")
+        StringBuilder sb = new StringBuilder();
+        sb.append("ALTER SQL_BLOCK_RULE ")
+                .append(ruleName)
+                .append(" \nPROPERTIES(\n")
+                .append(new PrintableMap<>(properties, " = ", true, true, 
true))
+                .append(")");
+        return sb.toString();
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateSqlBlockRuleStmt.java
 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateSqlBlockRuleStmt.java
new file mode 100644
index 0000000..efa599a
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateSqlBlockRuleStmt.java
@@ -0,0 +1,152 @@
+// 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.doris.analysis;
+
+import org.apache.doris.catalog.Catalog;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.FeNameFormat;
+import org.apache.doris.common.UserException;
+import org.apache.doris.common.util.PrintableMap;
+import org.apache.doris.common.util.Util;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.qe.ConnectContext;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Map;
+import java.util.Optional;
+
+/*
+ Create sqlBlockRule statement
+
+ syntax:
+      CREATE SQL_BLOCK_RULE `rule_name` PROPERTIES
+      (
+          sql = select * from a,
+          global = false
+          enable = true
+      )
+*/
+public class CreateSqlBlockRuleStmt extends DdlStmt {
+
+    public static final String SQL_PROPERTY = "sql";
+
+    public static final String SQL_HASH_PROPERTY = "sqlHash";
+
+    public static final String GLOBAL_PROPERTY = "global";
+
+    public static final String ENABLE_PROPERTY = "enable";
+
+    private final String ruleName;
+
+    private String sql;
+
+    private String sqlHash;
+
+    // whether effective global, default is false
+    private boolean global;
+
+    // whether to use the rule, default is true
+    private boolean enable;
+
+    private final Map<String, String> properties;
+
+    private static final String NAME_TYPE = "SQL BLOCK RULE NAME";
+
+    public static final ImmutableSet<String> PROPERTIES_SET = new 
ImmutableSet.Builder<String>()
+            .add(SQL_PROPERTY)
+            .add(SQL_HASH_PROPERTY)
+            .add(GLOBAL_PROPERTY)
+            .add(ENABLE_PROPERTY)
+            .build();
+
+    public CreateSqlBlockRuleStmt(String ruleName, Map<String, String> 
properties) {
+        this.ruleName = ruleName;
+        this.properties = properties;
+    }
+
+    @Override
+    public void analyze(Analyzer analyzer) throws UserException {
+        // check name
+        FeNameFormat.checkCommonName(NAME_TYPE, ruleName);
+        // check auth
+        if 
(!Catalog.getCurrentCatalog().getAuth().checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.ADMIN)) {
+            
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"ADMIN");
+        }
+        // check properties
+        CreateSqlBlockRuleStmt.checkCommonProperties(properties);
+        setProperties(properties);
+    }
+
+    private void setProperties(Map<String, String> properties) throws 
UserException {
+        this.sql = properties.get(SQL_PROPERTY);
+        this.sqlHash = properties.get(SQL_HASH_PROPERTY);
+        if ((StringUtils.isNotEmpty(sql) && StringUtils.isNotEmpty(sqlHash)) 
|| (StringUtils.isEmpty(sql) && StringUtils.isEmpty(sqlHash))) {
+            throw new AnalysisException("Only one sql or sqlHash can be 
configured");
+        }
+
+        this.global = 
Util.getBooleanPropertyOrDefault(properties.get(GLOBAL_PROPERTY), false, 
GLOBAL_PROPERTY + " should be a boolean");
+        this.enable = 
Util.getBooleanPropertyOrDefault(properties.get(ENABLE_PROPERTY), true, 
ENABLE_PROPERTY + " should be a boolean");
+    }
+
+    public static void checkCommonProperties(Map<String, String> properties) 
throws UserException {
+        if (properties == null || properties.isEmpty()) {
+            throw new AnalysisException("Not set properties");
+        }
+        Optional<String> optional = properties.keySet().stream().filter(
+                entity -> !PROPERTIES_SET.contains(entity)).findFirst();
+        if (optional.isPresent()) {
+            throw new AnalysisException(optional.get() + " is invalid 
property");
+        }
+    }
+
+    public String getRuleName() {
+        return ruleName;
+    }
+
+    public String getSql() {
+        return sql;
+    }
+
+    public String getSqlHash() {
+        return sqlHash;
+    }
+
+    public boolean isGlobal() {
+        return global;
+    }
+
+    public boolean isEnable() {
+        return enable;
+    }
+
+    @Override
+    public String toSql() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("CREATE SQL_BLOCK_RULE ")
+                .append(ruleName)
+                .append(" \nPROPERTIES(\n")
+                .append(new PrintableMap<>(properties, " = ", true, true, 
true))
+                .append(")");
+        return sb.toString();
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/DropSqlBlockRuleStmt.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/DropSqlBlockRuleStmt.java
new file mode 100644
index 0000000..e885a10
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/DropSqlBlockRuleStmt.java
@@ -0,0 +1,58 @@
+// 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.doris.analysis;
+
+import org.apache.doris.catalog.Catalog;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.UserException;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.qe.ConnectContext;
+
+import org.apache.parquet.Strings;
+
+import java.util.List;
+
+public class DropSqlBlockRuleStmt extends DdlStmt {
+
+    private List<String> ruleNames;
+
+    @Override
+    public void analyze(Analyzer analyzer) throws UserException {
+        super.analyze(analyzer);
+        // check auth
+        if 
(!Catalog.getCurrentCatalog().getAuth().checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.ADMIN)) {
+            
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"ADMIN");
+        }
+    }
+
+    public DropSqlBlockRuleStmt(List<String> ruleNames) {
+        this.ruleNames = ruleNames;
+    }
+
+    public List<String> getRuleNames() {
+        return ruleNames;
+    }
+
+    @Override
+    public String toSql() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("DROP SQL_BLOCK_RULE ").append(Strings.join(ruleNames, ","));
+        return sb.toString();
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowSqlBlockRuleStmt.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowSqlBlockRuleStmt.java
new file mode 100644
index 0000000..c93237f
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowSqlBlockRuleStmt.java
@@ -0,0 +1,83 @@
+// 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.doris.analysis;
+
+import org.apache.doris.catalog.Catalog;
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.ScalarType;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.UserException;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.ShowResultSetMetaData;
+
+import org.apache.commons.lang3.StringUtils;
+
+/*
+ Create sqlBlockRule statement
+
+ syntax:
+      show sql_block_rule
+      show sql_block_rule for rule_name
+*/
+public class ShowSqlBlockRuleStmt extends ShowStmt {
+
+    private static final ShowResultSetMetaData META_DATA =
+            ShowResultSetMetaData.builder()
+                    .addColumn(new Column("Name", 
ScalarType.createVarchar(50)))
+                    .addColumn(new Column("Sql", 
ScalarType.createVarchar(65535)))
+                    .addColumn(new Column("SqlHash", 
ScalarType.createVarchar(65535)))
+                    .addColumn(new Column("Global", 
ScalarType.createVarchar(4)))
+                    .addColumn(new Column("Enable", 
ScalarType.createVarchar(4)))
+                    .build();
+
+    private String ruleName; // optional
+
+    public ShowSqlBlockRuleStmt(String ruleName) {
+        this.ruleName = ruleName;
+    }
+
+    public String getRuleName() {
+        return ruleName;
+    }
+
+    @Override
+    public void analyze(Analyzer analyzer) throws UserException {
+        super.analyze(analyzer);
+        // check auth
+        if 
(!Catalog.getCurrentCatalog().getAuth().checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.ADMIN)) {
+            
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"ADMIN");
+        }
+    }
+
+    @Override
+    public ShowResultSetMetaData getMetaData() {
+        return META_DATA;
+    }
+
+    @Override
+    public String toSql() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("SHOW SQL_BLOCK_RULE");
+        if (StringUtils.isNotEmpty(ruleName)) {
+            sb.append(" FOR ").append(ruleName);
+        }
+        return sb.toString();
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/blockrule/SqlBlockRule.java 
b/fe/fe-core/src/main/java/org/apache/doris/blockrule/SqlBlockRule.java
new file mode 100644
index 0000000..13bbfbe
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/blockrule/SqlBlockRule.java
@@ -0,0 +1,146 @@
+// 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.doris.blockrule;
+
+import org.apache.doris.analysis.AlterSqlBlockRuleStmt;
+import org.apache.doris.analysis.CreateSqlBlockRuleStmt;
+import org.apache.doris.common.io.Text;
+import org.apache.doris.common.io.Writable;
+import org.apache.doris.persist.gson.GsonUtils;
+
+import com.google.common.collect.Lists;
+import com.google.gson.annotations.SerializedName;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class SqlBlockRule implements Writable {
+
+    public static final String NAME_TYPE = "SQL BLOCK RULE NAME";
+
+    public static final String DEFAULT_USER = "default";
+
+    // the rule name, cluster unique
+    @SerializedName(value = "name")
+    private String name;
+
+    @SerializedName(value = "sql")
+    private String sql;
+
+    // sql md5
+    @SerializedName(value = "sqlHash")
+    private String sqlHash;
+
+    // whether effective global
+    @SerializedName(value = "global")
+    private Boolean global;
+
+    // whether to use the rule
+    @SerializedName(value = "enable")
+    private Boolean enable;
+
+    private Pattern sqlPattern;
+
+    public SqlBlockRule(String name) {
+        this.name = name;
+    }
+
+    public SqlBlockRule(String name, String sql, String sqlHash, Boolean 
global, Boolean enable) {
+        this.name = name;
+        this.sql = sql;
+        this.sqlHash = sqlHash;
+        this.global = global;
+        this.enable = enable;
+        if (StringUtils.isNotEmpty(sql)) {
+            this.sqlPattern = Pattern.compile(sql);
+        }
+    }
+
+    public static SqlBlockRule fromCreateStmt(CreateSqlBlockRuleStmt stmt) {
+        return new SqlBlockRule(stmt.getRuleName(), stmt.getSql(), 
stmt.getSqlHash(), stmt.isGlobal(), stmt.isEnable());
+    }
+
+    public static SqlBlockRule fromAlterStmt(AlterSqlBlockRuleStmt stmt) {
+        return new SqlBlockRule(stmt.getRuleName(), stmt.getSql(), 
stmt.getSqlHash(), stmt.getGlobal(), stmt.getEnable());
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getSql() {
+        return sql;
+    }
+
+    public Pattern getSqlPattern() {
+        return sqlPattern;
+    }
+
+    public String getSqlHash() {
+        return sqlHash;
+    }
+
+    public Boolean getGlobal() {
+        return global;
+    }
+
+    public Boolean getEnable() {
+        return enable;
+    }
+
+    public void setSql(String sql) {
+        this.sql = sql;
+    }
+
+    public void setSqlPattern(Pattern sqlPattern) {
+        this.sqlPattern = sqlPattern;
+    }
+
+    public void setSqlHash(String sqlHash) {
+        this.sqlHash = sqlHash;
+    }
+
+    public void setGlobal(Boolean global) {
+        this.global = global;
+    }
+
+    public void setEnable(Boolean enable) {
+        this.enable = enable;
+    }
+
+    public List<String> getShowInfo() {
+        return Lists.newArrayList(this.name, this.sql, this.sqlHash, 
String.valueOf(this.global), String.valueOf(this.enable));
+    }
+
+    @Override
+    public void write(DataOutput out) throws IOException {
+        Text.writeString(out, GsonUtils.GSON.toJson(this));
+    }
+
+    public static SqlBlockRule read(DataInput in) throws IOException {
+        String json = Text.readString(in);
+        SqlBlockRule sqlBlockRule = GsonUtils.GSON.fromJson(json, 
SqlBlockRule.class);
+        sqlBlockRule.setSqlPattern(Pattern.compile(sqlBlockRule.getSql()));
+        return sqlBlockRule;
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/blockrule/SqlBlockRuleMgr.java 
b/fe/fe-core/src/main/java/org/apache/doris/blockrule/SqlBlockRuleMgr.java
new file mode 100644
index 0000000..fa454a6
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/blockrule/SqlBlockRuleMgr.java
@@ -0,0 +1,205 @@
+// 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.doris.blockrule;
+
+import org.apache.doris.analysis.AlterSqlBlockRuleStmt;
+import org.apache.doris.analysis.CreateSqlBlockRuleStmt;
+import org.apache.doris.analysis.DropSqlBlockRuleStmt;
+import org.apache.doris.analysis.ShowSqlBlockRuleStmt;
+import org.apache.doris.catalog.Catalog;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.DdlException;
+import org.apache.doris.common.UserException;
+import org.apache.doris.common.io.Text;
+import org.apache.doris.common.io.Writable;
+import org.apache.doris.metric.MetricRepo;
+import org.apache.doris.persist.gson.GsonUtils;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.gson.annotations.SerializedName;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.stream.Collectors;
+
+public class SqlBlockRuleMgr implements Writable {
+    private static final Logger LOG = 
LogManager.getLogger(SqlBlockRuleMgr.class);
+
+    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
+
+    @SerializedName(value = "nameToSqlBlockRuleMap")
+    private Map<String, SqlBlockRule> nameToSqlBlockRuleMap = 
Maps.newConcurrentMap();
+
+    private void writeLock() {
+        lock.writeLock().lock();
+    }
+
+    private void writeUnlock() {
+        lock.writeLock().unlock();
+    }
+
+    public boolean existRule(String name) {
+        return nameToSqlBlockRuleMap.containsKey(name);
+    }
+
+    public List<SqlBlockRule> getSqlBlockRule(ShowSqlBlockRuleStmt stmt) 
throws AnalysisException {
+        String ruleName = stmt.getRuleName();
+        if (StringUtils.isNotEmpty(ruleName)) {
+            if (nameToSqlBlockRuleMap.containsKey(ruleName)) {
+                SqlBlockRule sqlBlockRule = 
nameToSqlBlockRuleMap.get(ruleName);
+                return Lists.newArrayList(sqlBlockRule);
+            }
+            return Lists.newArrayList();
+        }
+        return Lists.newArrayList(nameToSqlBlockRuleMap.values());
+    }
+
+    public void createSqlBlockRule(CreateSqlBlockRuleStmt stmt) throws 
UserException {
+        writeLock();
+        try {
+            SqlBlockRule sqlBlockRule = SqlBlockRule.fromCreateStmt(stmt);
+            String ruleName = sqlBlockRule.getName();
+            if (existRule(ruleName)) {
+                throw new DdlException("the sql block rule " + ruleName + " 
already create");
+            }
+            unprotectedAdd(sqlBlockRule);
+            
Catalog.getCurrentCatalog().getEditLog().logCreateSqlBlockRule(sqlBlockRule);
+        } finally {
+            writeUnlock();
+        }
+    }
+
+    public void replayCreate(SqlBlockRule sqlBlockRule) {
+        unprotectedAdd(sqlBlockRule);
+        LOG.info("replay create sql block rule: {}", sqlBlockRule);
+    }
+
+    public void alterSqlBlockRule(AlterSqlBlockRuleStmt stmt) throws 
DdlException {
+        writeLock();
+        try {
+            SqlBlockRule sqlBlockRule = SqlBlockRule.fromAlterStmt(stmt);
+            String ruleName = sqlBlockRule.getName();
+            if (!existRule(ruleName)) {
+                throw new DdlException("the sql block rule " + ruleName + " 
not exist");
+            }
+            SqlBlockRule originRule = nameToSqlBlockRuleMap.get(ruleName);
+            if (StringUtils.isEmpty(sqlBlockRule.getSql())) {
+                sqlBlockRule.setSql(originRule.getSql());
+            }
+            if (StringUtils.isEmpty(sqlBlockRule.getSqlHash())) {
+                sqlBlockRule.setSqlHash(originRule.getSqlHash());
+            }
+            if (sqlBlockRule.getGlobal() == null) {
+                sqlBlockRule.setGlobal(originRule.getGlobal());
+            }
+            if (sqlBlockRule.getEnable() == null) {
+                sqlBlockRule.setEnable(originRule.getEnable());
+            }
+            unprotectedUpdate(sqlBlockRule);
+            
Catalog.getCurrentCatalog().getEditLog().logAlterSqlBlockRule(sqlBlockRule);
+        } finally {
+            writeUnlock();
+        }
+    }
+
+    public void replayAlter(SqlBlockRule sqlBlockRule) {
+        unprotectedUpdate(sqlBlockRule);
+        LOG.info("replay alter sql block rule: {}", sqlBlockRule);
+    }
+
+    public void unprotectedUpdate(SqlBlockRule sqlBlockRule) {
+        nameToSqlBlockRuleMap.put(sqlBlockRule.getName(), sqlBlockRule);
+    }
+
+    public void unprotectedAdd(SqlBlockRule sqlBlockRule) {
+        nameToSqlBlockRuleMap.put(sqlBlockRule.getName(), sqlBlockRule);
+    }
+
+    public void dropSqlBlockRule(DropSqlBlockRuleStmt stmt) throws 
DdlException {
+        writeLock();
+        try {
+            List<String> ruleNames = stmt.getRuleNames();
+            for (String ruleName : ruleNames) {
+                if (!existRule(ruleName)) {
+                    throw new DdlException("the sql block rule " + ruleName + 
" not exist");
+                }
+            }
+            unprotectedDrop(ruleNames);
+            
Catalog.getCurrentCatalog().getEditLog().logDropSqlBlockRule(ruleNames);
+        } finally {
+            writeUnlock();
+        }
+    }
+
+    public void replayDrop(List<String> ruleNames) {
+        unprotectedDrop(ruleNames);
+        LOG.info("replay drop sql block ruleNames: {}", ruleNames);
+    }
+
+    public void unprotectedDrop(List<String> ruleNames) {
+        ruleNames.forEach(name -> nameToSqlBlockRuleMap.remove(name));
+    }
+
+    public void matchSql(String originSql, String sqlHash, String user) throws 
AnalysisException {
+        // match global rule
+        List<SqlBlockRule> globalRules = 
nameToSqlBlockRuleMap.values().stream().filter(SqlBlockRule::getGlobal).collect(Collectors.toList());
+        for (SqlBlockRule rule : globalRules) {
+            matchSql(rule, originSql, sqlHash);
+        }
+        // match user rule
+        String[] bindSqlBlockRules = 
Catalog.getCurrentCatalog().getAuth().getSqlBlockRules(user);
+        for (String ruleName : bindSqlBlockRules) {
+            SqlBlockRule rule = nameToSqlBlockRuleMap.get(ruleName);
+            if (rule == null) {
+                continue;
+            }
+            matchSql(rule, originSql, sqlHash);
+        }
+    }
+
+    public void matchSql(SqlBlockRule rule, String originSql, String sqlHash) 
throws AnalysisException {
+        if (rule.getEnable()) {
+            if (StringUtils.isNotEmpty(rule.getSqlHash()) && 
rule.getSqlHash().equals(sqlHash)) {
+                MetricRepo.COUNTER_HIT_SQL_BLOCK_RULE.increase(1L);
+                throw new AnalysisException("sql match hash sql block rule: " 
+ rule.getName());
+            } else if (StringUtils.isNotEmpty(rule.getSql()) && 
rule.getSqlPattern().matcher(originSql).find()) {
+                MetricRepo.COUNTER_HIT_SQL_BLOCK_RULE.increase(1L);
+                throw new AnalysisException("sql match regex sql block rule: " 
+ rule.getName());
+            }
+        }
+    }
+
+    @Override
+    public void write(DataOutput out) throws IOException {
+        Text.writeString(out, GsonUtils.GSON.toJson(this));
+    }
+
+    public static SqlBlockRuleMgr read(DataInput in) throws IOException {
+        String json = Text.readString(in);
+        return GsonUtils.GSON.fromJson(json, SqlBlockRuleMgr.class);
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Catalog.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/Catalog.java
index fdc9147..0b49a6d 100755
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Catalog.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Catalog.java
@@ -85,6 +85,7 @@ import org.apache.doris.analysis.UninstallPluginStmt;
 import org.apache.doris.analysis.UserDesc;
 import org.apache.doris.analysis.UserIdentity;
 import org.apache.doris.backup.BackupHandler;
+import org.apache.doris.blockrule.SqlBlockRuleMgr;
 import org.apache.doris.catalog.ColocateTableIndex.GroupId;
 import org.apache.doris.catalog.Database.DbState;
 import org.apache.doris.catalog.DistributionInfo.DistributionInfoType;
@@ -308,6 +309,7 @@ public class Catalog {
     private LoadManager loadManager;
     private StreamLoadRecordMgr streamLoadRecordMgr;
     private RoutineLoadManager routineLoadManager;
+    private SqlBlockRuleMgr sqlBlockRuleMgr;
     private ExportMgr exportMgr;
     private SyncJobManager syncJobManager;
     private Alter alter;
@@ -495,6 +497,7 @@ public class Catalog {
         this.fullNameToDb = new ConcurrentHashMap<>();
         this.load = new Load();
         this.routineLoadManager = new RoutineLoadManager();
+        this.sqlBlockRuleMgr = new SqlBlockRuleMgr();
         this.exportMgr = new ExportMgr();
         this.syncJobManager = new SyncJobManager();
         this.alter = new Alter();
@@ -1901,6 +1904,14 @@ public class Catalog {
         return checksum;
     }
 
+    public long loadSqlBlockRule(DataInputStream in, long checksum) throws 
IOException {
+        if (Catalog.getCurrentCatalogJournalVersion() >= 
FeMetaVersion.VERSION_104) {
+            sqlBlockRuleMgr = SqlBlockRuleMgr.read(in);
+        }
+        LOG.info("finished replay sqlBlockRule from image");
+        return checksum;
+    }
+
     // Only called by checkpoint thread
     public void saveImage() throws IOException {
         // Write image.ckpt
@@ -2174,6 +2185,11 @@ public class Catalog {
         return checksum;
     }
 
+    public long saveSqlBlockRule(DataOutputStream out, long checksum) throws 
IOException {
+        Catalog.getCurrentCatalog().getSqlBlockRuleMgr().write(out);
+        return checksum;
+    }
+
     public void createLabelCleaner() {
         labelCleaner = new MasterDaemon("LoadLabelCleaner", 
Config.label_clean_interval_second * 1000L) {
             @Override
@@ -4864,6 +4880,10 @@ public class Catalog {
         return routineLoadManager;
     }
 
+    public SqlBlockRuleMgr getSqlBlockRuleMgr() {
+        return sqlBlockRuleMgr;
+    }
+
     public RoutineLoadTaskScheduler getRoutineLoadTaskScheduler(){
         return routineLoadTaskScheduler;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/common/FeMetaVersion.java 
b/fe/fe-core/src/main/java/org/apache/doris/common/FeMetaVersion.java
index 149f3e1..0aec044 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/FeMetaVersion.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/FeMetaVersion.java
@@ -218,6 +218,8 @@ public final class FeMetaVersion {
     public static final int VERSION_102 = 102;
     // support sync job
     public static final int VERSION_103 = 103;
+    // add sql block rule to deny specified sql
+    public static final int VERSION_104 = 104;
     // note: when increment meta version, should assign the latest version to 
VERSION_CURRENT
-    public static final int VERSION_CURRENT = VERSION_103;
+    public static final int VERSION_CURRENT = VERSION_104;
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/MetaReader.java 
b/fe/fe-core/src/main/java/org/apache/doris/common/MetaReader.java
index e0e30c1..be646f9 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/MetaReader.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/MetaReader.java
@@ -102,6 +102,7 @@ public class MetaReader {
             checksum = catalog.loadSmallFiles(dis, checksum);
             checksum = catalog.loadPlugins(dis, checksum);
             checksum = catalog.loadDeleteHandler(dis, checksum);
+            checksum = catalog.loadSqlBlockRule(dis, checksum);
         }
 
         MetaFooter metaFooter = MetaFooter.read(imageFile);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/MetaWriter.java 
b/fe/fe-core/src/main/java/org/apache/doris/common/MetaWriter.java
index b591d08..a58f4f5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/MetaWriter.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/MetaWriter.java
@@ -122,6 +122,8 @@ public class MetaWriter {
             checksum = catalog.savePlugins(dos, checksum);
             metaIndices.add(new MetaIndex("deleteHandler", dos.getCount()));
             checksum = catalog.saveDeleteHandler(dos, checksum);
+            metaIndices.add(new MetaIndex("sqlBlockRule", dos.getCount()));
+            checksum = catalog.saveSqlBlockRule(dos, checksum);
         }
         MetaFooter.write(imageFile, metaIndices, checksum);
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java 
b/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java
index 9f9b3b9..4bdb054 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java
@@ -24,6 +24,7 @@ import org.apache.doris.analysis.UserIdentity;
 import org.apache.doris.backup.BackupJob;
 import org.apache.doris.backup.Repository;
 import org.apache.doris.backup.RestoreJob;
+import org.apache.doris.blockrule.SqlBlockRule;
 import org.apache.doris.catalog.BrokerMgr;
 import org.apache.doris.catalog.Database;
 import org.apache.doris.catalog.EncryptKey;
@@ -66,6 +67,7 @@ import org.apache.doris.persist.DropInfo;
 import org.apache.doris.persist.DropLinkDbAndUpdateDbInfo;
 import org.apache.doris.persist.DropPartitionInfo;
 import org.apache.doris.persist.DropResourceOperationLog;
+import org.apache.doris.persist.DropSqlBlockRuleOperationLog;
 import org.apache.doris.persist.GlobalVarPersistInfo;
 import org.apache.doris.persist.HbPackage;
 import org.apache.doris.persist.LdapInfo;
@@ -638,6 +640,21 @@ public class JournalEntity implements Writable {
                 isRead = true;
                 break;
             }
+            case OperationType.OP_CREATE_SQL_BLOCK_RULE: {
+                data = SqlBlockRule.read(in);
+                isRead = true;
+                break;
+            }
+            case OperationType.OP_ALTER_SQL_BLOCK_RULE: {
+                data = SqlBlockRule.read(in);
+                isRead = true;
+                break;
+            }
+            case OperationType.OP_DROP_SQL_BLOCK_RULE: {
+                data = DropSqlBlockRuleOperationLog.read(in);
+                isRead = true;
+                break;
+            }
             default: {
                 IOException e = new IOException();
                 LOG.error("UNKNOWN Operation Type {}", opCode, e);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/metric/MetricRepo.java 
b/fe/fe-core/src/main/java/org/apache/doris/metric/MetricRepo.java
index 50a50d0..968a024 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/metric/MetricRepo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/metric/MetricRepo.java
@@ -92,6 +92,7 @@ public final class MetricRepo {
     public static LongCounterMetric COUNTER_ROUTINE_LOAD_ROWS;
     public static LongCounterMetric COUNTER_ROUTINE_LOAD_RECEIVED_BYTES;
     public static LongCounterMetric COUNTER_ROUTINE_LOAD_ERROR_ROWS;
+    public static LongCounterMetric COUNTER_HIT_SQL_BLOCK_RULE;
 
     public static Histogram HISTO_QUERY_LATENCY;
     public static Histogram HISTO_EDIT_LOG_WRITE_LATENCY;
@@ -302,6 +303,9 @@ public final class MetricRepo {
                 "total error rows of routine load");
         PALO_METRIC_REGISTER.addPaloMetrics(COUNTER_ROUTINE_LOAD_ERROR_ROWS);
 
+        COUNTER_HIT_SQL_BLOCK_RULE = new 
LongCounterMetric("counter_hit_sql_block_rule", MetricUnit.ROWS,
+                "total hit sql block rule query");
+        PALO_METRIC_REGISTER.addPaloMetrics(COUNTER_HIT_SQL_BLOCK_RULE);
         // 3. histogram
         HISTO_QUERY_LATENCY = 
METRIC_REGISTER.histogram(MetricRegistry.name("query", "latency", "ms"));
         HISTO_EDIT_LOG_WRITE_LATENCY = 
METRIC_REGISTER.histogram(MetricRegistry.name("editlog", "write", "latency", 
"ms"));
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/CommonUserProperties.java
 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/CommonUserProperties.java
index 0695f8a..8ab5ebe 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/CommonUserProperties.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/CommonUserProperties.java
@@ -35,6 +35,9 @@ public class CommonUserProperties implements Writable {
     private long maxConn = 100;
     @SerializedName("maxQueryInstances")
     private long maxQueryInstances = -1;
+    @SerializedName("sqlBlockRules")
+    private String sqlBlockRules = "";
+    private String[] sqlBlockRulesSplit = {};
 
     long getMaxConn() {
         return maxConn;
@@ -44,6 +47,14 @@ public class CommonUserProperties implements Writable {
         return maxQueryInstances;
     }
 
+    String getSqlBlockRules() {
+        return sqlBlockRules;
+    }
+    
+    String[] getSqlBlockRulesSplit() {
+        return sqlBlockRulesSplit;
+    }
+
     void setMaxConn(long maxConn) {
         this.maxConn = maxConn;
     }
@@ -52,9 +63,22 @@ public class CommonUserProperties implements Writable {
         this.maxQueryInstances = maxQueryInstances;
     }
 
+    void setSqlBlockRules(String sqlBlockRules) {
+        this.sqlBlockRules = sqlBlockRules;
+        setSqlBlockRulesSplit(sqlBlockRules);
+    }
+    
+    void setSqlBlockRulesSplit(String sqlBlockRules) {
+        // split
+        this.sqlBlockRulesSplit = sqlBlockRules.replace(" ", "").split(",");
+    }
+
     public static CommonUserProperties read(DataInput in) throws IOException {
         String json = Text.readString(in);
-        return GsonUtils.GSON.fromJson(json, CommonUserProperties.class);
+        CommonUserProperties commonUserProperties = 
GsonUtils.GSON.fromJson(json, CommonUserProperties.class);
+        // trigger split
+        
commonUserProperties.setSqlBlockRulesSplit(commonUserProperties.getSqlBlockRules());
+        return commonUserProperties;
     }
 
     @Override
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PaloAuth.java 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PaloAuth.java
index 209ed36..836ce39 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PaloAuth.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PaloAuth.java
@@ -50,14 +50,14 @@ import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.thrift.TFetchResourceResult;
 import org.apache.doris.thrift.TPrivilegeStatus;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
 import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
 import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.IOException;
@@ -1134,6 +1134,15 @@ public class PaloAuth implements Writable {
         }
     }
 
+    public String[] getSqlBlockRules(String qualifiedUser) {
+        readLock();
+        try {
+            return propertyMgr.getSqlBlockRules(qualifiedUser);
+        } finally {
+            readUnlock();
+        }
+    }
+
     public void getAllDomains(Set<String> allDomains) {
         readLock();
         try {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/UserProperty.java 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/UserProperty.java
index 57578ae..f3a7867 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/UserProperty.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/UserProperty.java
@@ -66,6 +66,7 @@ public class UserProperty implements Writable {
     private static final String PROP_QUOTA = "quota";
     private static final String PROP_DEFAULT_LOAD_CLUSTER = 
"default_load_cluster";
     private static final String PROP_LOAD_CLUSTER = "load_cluster";
+    private static final String PROP_SQL_BLOCK_RULES = "sql_block_rules";
 
     // for system user
     public static final Set<Pattern> ADVANCED_PROPERTIES = Sets.newHashSet();
@@ -95,6 +96,7 @@ public class UserProperty implements Writable {
         ADVANCED_PROPERTIES.add(Pattern.compile("^" + PROP_LOAD_CLUSTER + "." 
+ DppConfig.CLUSTER_NAME_REGEX + "."
                 + DppConfig.PRIORITY + "$", Pattern.CASE_INSENSITIVE));
         ADVANCED_PROPERTIES.add(Pattern.compile("^" + PROP_MAX_QUERY_INSTANCES 
+ "$", Pattern.CASE_INSENSITIVE));
+        ADVANCED_PROPERTIES.add(Pattern.compile("^" + PROP_SQL_BLOCK_RULES + 
"$", Pattern.CASE_INSENSITIVE));
 
         COMMON_PROPERTIES.add(Pattern.compile("^" + PROP_QUOTA + ".", 
Pattern.CASE_INSENSITIVE));
         COMMON_PROPERTIES.add(Pattern.compile("^" + PROP_DEFAULT_LOAD_CLUSTER 
+ "$", Pattern.CASE_INSENSITIVE));
@@ -121,6 +123,10 @@ public class UserProperty implements Writable {
         return commonProperties.getMaxQueryInstances();// maxQueryInstances;
     }
 
+    public String[] getSqlBlockRules() {
+        return commonProperties.getSqlBlockRulesSplit();
+    }
+
     public WhiteList getWhiteList() {
         return whiteList;
     }
@@ -143,6 +149,7 @@ public class UserProperty implements Writable {
         // copy
         long newMaxConn = this.commonProperties.getMaxConn();
         long newMaxQueryInstances = 
this.commonProperties.getMaxQueryInstances();
+        String sqlBlockRules = this.commonProperties.getSqlBlockRules();
         UserResource newResource = resource.getCopiedUserResource();
         String newDefaultLoadCluster = defaultLoadCluster;
         Map<String, DppConfig> newDppConfigs = 
Maps.newHashMap(clusterToDppConfig);
@@ -227,6 +234,12 @@ public class UserProperty implements Writable {
                 } catch (NumberFormatException e) {
                     throw new DdlException(PROP_MAX_QUERY_INSTANCES + " is not 
number");
                 }
+            } else if (keyArr[0].equalsIgnoreCase(PROP_SQL_BLOCK_RULES)) {
+                // set property "sql_block_rules" = "test_rule1,test_rule2"
+                if (keyArr.length != 1) {
+                    throw new DdlException(PROP_SQL_BLOCK_RULES + " format 
error");
+                }
+                sqlBlockRules = value;
             } else {
                 throw new DdlException("Unknown user property(" + key + ")");
             }
@@ -235,6 +248,7 @@ public class UserProperty implements Writable {
         // set
         this.commonProperties.setMaxConn(newMaxConn);
         this.commonProperties.setMaxQueryInstances(newMaxQueryInstances);
+        this.commonProperties.setSqlBlockRules(sqlBlockRules);
         resource = newResource;
         if (newDppConfigs.containsKey(newDefaultLoadCluster)) {
             defaultLoadCluster = newDefaultLoadCluster;
@@ -322,6 +336,9 @@ public class UserProperty implements Writable {
         // max query instance
         result.add(Lists.newArrayList(PROP_MAX_QUERY_INSTANCES, 
String.valueOf(commonProperties.getMaxQueryInstances())));
 
+        // sql block rules
+        result.add(Lists.newArrayList(PROP_SQL_BLOCK_RULES, 
commonProperties.getSqlBlockRules()));
+
         // resource
         ResourceGroup group = resource.getResource();
         for (Map.Entry<ResourceType, Integer> entry : 
group.getQuotaMap().entrySet()) {
@@ -397,9 +414,7 @@ public class UserProperty implements Writable {
         userProperty.readFields(in);
         return userProperty;
     }
-
-
-
+    
     @Override
     public void write(DataOutput out) throws IOException {
         // user name
@@ -433,7 +448,7 @@ public class UserProperty implements Writable {
             // consume the flag of empty user name
             in.readBoolean();
         }
-            
+        
         // user name
         if (Catalog.getCurrentCatalogJournalVersion() < 
FeMetaVersion.VERSION_30) {
             qualifiedUser = 
ClusterNamespace.getFullName(SystemInfoService.DEFAULT_CLUSTER, 
Text.readString(in));
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/UserPropertyMgr.java
 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/UserPropertyMgr.java
index 5f9cde8..c681d38 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/UserPropertyMgr.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/UserPropertyMgr.java
@@ -220,6 +220,14 @@ public class UserPropertyMgr implements Writable {
         }
     }
 
+    public String[] getSqlBlockRules(String qualifiedUser) {
+        UserProperty existProperty = propertyMap.get(qualifiedUser);
+        if (existProperty == null) {
+            return new String[]{};
+        }
+        return existProperty.getSqlBlockRules();
+    }
+
     public UserProperty getUserProperty(String qualifiedUserName) {
         return propertyMap.get(qualifiedUserName);
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/CommonUserProperties.java
 
b/fe/fe-core/src/main/java/org/apache/doris/persist/DropSqlBlockRuleOperationLog.java
similarity index 58%
copy from 
fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/CommonUserProperties.java
copy to 
fe/fe-core/src/main/java/org/apache/doris/persist/DropSqlBlockRuleOperationLog.java
index 0695f8a..f3d64a8 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/CommonUserProperties.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/persist/DropSqlBlockRuleOperationLog.java
@@ -15,7 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.mysql.privilege;
+package org.apache.doris.persist;
 
 import org.apache.doris.common.io.Text;
 import org.apache.doris.common.io.Writable;
@@ -26,40 +26,30 @@ import com.google.gson.annotations.SerializedName;
 import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.IOException;
+import java.util.List;
 
 /**
- * Used in
+ * For user sql_block_rule drop
  */
-public class CommonUserProperties implements Writable {
-    @SerializedName("maxConn")
-    private long maxConn = 100;
-    @SerializedName("maxQueryInstances")
-    private long maxQueryInstances = -1;
+public class DropSqlBlockRuleOperationLog implements Writable {
 
-    long getMaxConn() {
-        return maxConn;
-    }
-
-    long getMaxQueryInstances() {
-        return maxQueryInstances;
-    }
+    @SerializedName(value = "ruleNames")
+    private List<String> ruleNames;
 
-    void setMaxConn(long maxConn) {
-        this.maxConn = maxConn;
+    public DropSqlBlockRuleOperationLog(List<String> ruleNames) {
+        this.ruleNames = ruleNames;
     }
 
-    void setMaxQueryInstances(long maxQueryInstances) {
-        this.maxQueryInstances = maxQueryInstances;
-    }
-
-    public static CommonUserProperties read(DataInput in) throws IOException {
-        String json = Text.readString(in);
-        return GsonUtils.GSON.fromJson(json, CommonUserProperties.class);
+    public List<String> getRuleNames() {
+        return ruleNames;
     }
 
     @Override
     public void write(DataOutput out) throws IOException {
-        String json = GsonUtils.GSON.toJson(this);
-        Text.writeString(out, json);
+        Text.writeString(out, GsonUtils.GSON.toJson(this));
+    }
+
+    public static DropSqlBlockRuleOperationLog read(DataInput in) throws 
IOException {
+        return GsonUtils.GSON.fromJson(Text.readString(in), 
DropSqlBlockRuleOperationLog.class);
     }
-}
\ No newline at end of file
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java 
b/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java
index dd029f2..c0f562b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java
@@ -26,6 +26,7 @@ import org.apache.doris.analysis.UserIdentity;
 import org.apache.doris.backup.BackupJob;
 import org.apache.doris.backup.Repository;
 import org.apache.doris.backup.RestoreJob;
+import org.apache.doris.blockrule.SqlBlockRule;
 import org.apache.doris.catalog.BrokerMgr;
 import org.apache.doris.catalog.Catalog;
 import org.apache.doris.catalog.Database;
@@ -843,6 +844,21 @@ public class EditLog {
                     catalog.getAlterInstance().replayReplaceTable(log);
                     break;
                 }
+                case OperationType.OP_CREATE_SQL_BLOCK_RULE: {
+                    SqlBlockRule rule = (SqlBlockRule) journal.getData();
+                    catalog.getSqlBlockRuleMgr().replayCreate(rule);
+                    break;
+                }
+                case OperationType.OP_ALTER_SQL_BLOCK_RULE: {
+                    SqlBlockRule rule = (SqlBlockRule) journal.getData();
+                    catalog.getSqlBlockRuleMgr().replayAlter(rule);
+                    break;
+                }
+                case OperationType.OP_DROP_SQL_BLOCK_RULE: {
+                    DropSqlBlockRuleOperationLog log = 
(DropSqlBlockRuleOperationLog) journal.getData();
+                    
catalog.getSqlBlockRuleMgr().replayDrop(log.getRuleNames());
+                    break;
+                }
                 default: {
                     IOException e = new IOException();
                     LOG.error("UNKNOWN Operation Type {}", opCode, e);
@@ -1446,4 +1462,16 @@ public class EditLog {
     public void logModifyComment(ModifyCommentOperationLog op) {
         logEdit(OperationType.OP_MODIFY_COMMENT, op);
     }
+
+    public void logCreateSqlBlockRule(SqlBlockRule rule) {
+        logEdit(OperationType.OP_CREATE_SQL_BLOCK_RULE, rule);
+    }
+
+    public void logAlterSqlBlockRule(SqlBlockRule rule) {
+        logEdit(OperationType.OP_ALTER_SQL_BLOCK_RULE, rule);
+    }
+
+    public void logDropSqlBlockRule(List<String> ruleNames) {
+        logEdit(OperationType.OP_DROP_SQL_BLOCK_RULE, new 
DropSqlBlockRuleOperationLog(ruleNames));
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java 
b/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java
index 6d19457..3916a76 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java
@@ -209,6 +209,11 @@ public class OperationType {
 
     public static final short OP_SET_LDAP_PASSWORD = 290;
 
+    // sql block rule 300-310
+    public static final short OP_CREATE_SQL_BLOCK_RULE = 300;
+    public static final short OP_ALTER_SQL_BLOCK_RULE = 301;
+    public static final short OP_DROP_SQL_BLOCK_RULE = 302;
+
     // get opcode name by op codeStri
     public static String getOpName(short opCode) {
         try {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/plugin/AuditEvent.java 
b/fe/fe-core/src/main/java/org/apache/doris/plugin/AuditEvent.java
index af78918..0054e7e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/plugin/AuditEvent.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/plugin/AuditEvent.java
@@ -79,6 +79,8 @@ public class AuditEvent {
     public String stmt = "";
     @AuditField(value = "CpuTimeMS")
     public long cpuTimeMs = -1;
+    @AuditField(value = "SqlHash")
+    public String sqlHash = "";
 
     public static class AuditEventBuilder {
 
@@ -171,6 +173,11 @@ public class AuditEvent {
             return this;
         }
 
+        public AuditEventBuilder setSqlHash(String sqlHash) {
+            auditEvent.sqlHash = sqlHash;
+            return this;
+        }
+
         public AuditEvent build() {
             return this.auditEvent;
         }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java
index 16fc2d7..a39939c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java
@@ -116,6 +116,8 @@ public class ConnectContext {
     // If set to true, the nondeterministic function will not be rewrote to 
constant.
     private boolean notEvalNondeterministicFunction = false;
 
+    private String sqlHash;
+
     public static ConnectContext get() {
         return threadLocalInfo.get();
     }
@@ -419,6 +421,14 @@ public class ConnectContext {
         this.clusterName = clusterName;
     }
 
+    public String getSqlHash() {
+        return sqlHash;
+    }
+
+    public void setSqlHash(String sqlHash) {
+        this.sqlHash = sqlHash;
+    }
+
     // kill operation with no protect.
     public void kill(boolean killConnection) {
         LOG.warn("kill timeout query, {}, kill connection: {}",
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java
index 6ce8906..056b890 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java
@@ -53,6 +53,7 @@ import org.apache.doris.thrift.TUniqueId;
 import com.google.common.collect.Lists;
 import com.google.common.base.Strings;
 
+import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -143,7 +144,7 @@ public class ConnectProcessor {
         }
         
         
ctx.getAuditEventBuilder().setFeIp(FrontendOptions.getLocalHostAddress());
-
+        
         // We put origin query stmt at the end of audit log, for parsing the 
log more convenient.
         if (!ctx.getState().isQuery() && (parsedStmt != null && 
parsedStmt.needAuditEncryption())) {
             ctx.getAuditEventBuilder().setStmt(parsedStmt.toSql());
@@ -179,12 +180,22 @@ public class ConnectProcessor {
             ctx.getState().setError("Unsupported character set(UTF-8)");
             return;
         }
+        String sqlHash = DigestUtils.md5Hex(originStmt);
+        ctx.setSqlHash(sqlHash);
+        try {
+            
Catalog.getCurrentCatalog().getSqlBlockRuleMgr().matchSql(originStmt, sqlHash, 
ctx.getQualifiedUser());
+        } catch (AnalysisException e) {
+            LOG.warn(e.getMessage());
+            ctx.getState().setError(e.getMessage());
+            return;
+        }
         ctx.getAuditEventBuilder().reset();
         ctx.getAuditEventBuilder()
             .setTimestamp(System.currentTimeMillis())
             .setClientIp(ctx.getMysqlChannel().getRemoteHostPortString())
             .setUser(ctx.getQualifiedUser())
-            .setDb(ctx.getDatabase());
+            .setDb(ctx.getDatabase())
+            .setSqlHash(ctx.getSqlHash());
 
         // execute this query.
         StatementBase parsedStmt = null;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java
index 1d2a5f5..cbe1fb5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java
@@ -26,6 +26,7 @@ import org.apache.doris.analysis.AlterClusterStmt;
 import org.apache.doris.analysis.AlterDatabaseQuotaStmt;
 import org.apache.doris.analysis.AlterDatabaseRename;
 import org.apache.doris.analysis.AlterRoutineLoadStmt;
+import org.apache.doris.analysis.AlterSqlBlockRuleStmt;
 import org.apache.doris.analysis.AlterSystemStmt;
 import org.apache.doris.analysis.AlterTableStmt;
 import org.apache.doris.analysis.AlterViewStmt;
@@ -46,6 +47,7 @@ import org.apache.doris.analysis.CreateRepositoryStmt;
 import org.apache.doris.analysis.CreateResourceStmt;
 import org.apache.doris.analysis.CreateRoleStmt;
 import org.apache.doris.analysis.CreateRoutineLoadStmt;
+import org.apache.doris.analysis.CreateSqlBlockRuleStmt;
 import org.apache.doris.analysis.CreateTableLikeStmt;
 import org.apache.doris.analysis.CreateTableStmt;
 import org.apache.doris.analysis.CreateUserStmt;
@@ -61,6 +63,7 @@ import org.apache.doris.analysis.DropMaterializedViewStmt;
 import org.apache.doris.analysis.DropRepositoryStmt;
 import org.apache.doris.analysis.DropResourceStmt;
 import org.apache.doris.analysis.DropRoleStmt;
+import org.apache.doris.analysis.DropSqlBlockRuleStmt;
 import org.apache.doris.analysis.DropTableStmt;
 import org.apache.doris.analysis.DropUserStmt;
 import org.apache.doris.analysis.GrantStmt;
@@ -263,6 +266,12 @@ public class DdlExecutor {
             catalog.getSyncJobManager().stopSyncJob((StopSyncJobStmt) ddlStmt);
         } else if (ddlStmt instanceof AdminCleanTrashStmt) {
             catalog.cleanTrash((AdminCleanTrashStmt) ddlStmt);
+        } else if (ddlStmt instanceof CreateSqlBlockRuleStmt) {
+            
catalog.getSqlBlockRuleMgr().createSqlBlockRule((CreateSqlBlockRuleStmt) 
ddlStmt);
+        } else if (ddlStmt instanceof AlterSqlBlockRuleStmt) {
+            
catalog.getSqlBlockRuleMgr().alterSqlBlockRule((AlterSqlBlockRuleStmt) ddlStmt);
+        } else if (ddlStmt instanceof DropSqlBlockRuleStmt) {
+            
catalog.getSqlBlockRuleMgr().dropSqlBlockRule((DropSqlBlockRuleStmt) ddlStmt);
         } else {
             throw new DdlException("Unknown statement.");
         }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java
index 83b0073..df776fb 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java
@@ -68,6 +68,7 @@ import org.apache.doris.analysis.ShowRoutineLoadStmt;
 import org.apache.doris.analysis.ShowRoutineLoadTaskStmt;
 import org.apache.doris.analysis.ShowSmallFilesStmt;
 import org.apache.doris.analysis.ShowSnapshotStmt;
+import org.apache.doris.analysis.ShowSqlBlockRuleStmt;
 import org.apache.doris.analysis.ShowStmt;
 import org.apache.doris.analysis.ShowStreamLoadStmt;
 import org.apache.doris.analysis.ShowSyncJobStmt;
@@ -85,6 +86,7 @@ import org.apache.doris.backup.AbstractJob;
 import org.apache.doris.backup.BackupJob;
 import org.apache.doris.backup.Repository;
 import org.apache.doris.backup.RestoreJob;
+import org.apache.doris.blockrule.SqlBlockRule;
 import org.apache.doris.catalog.BrokerMgr;
 import org.apache.doris.catalog.Catalog;
 import org.apache.doris.catalog.Column;
@@ -313,6 +315,8 @@ public class ShowExecutor {
             handleAdminShowDataSkew();
         } else if (stmt instanceof ShowSyncJobStmt) {
             handleShowSyncJobs();
+        } else if (stmt instanceof ShowSqlBlockRuleStmt) {
+            handleShowSqlBlockRule();
         } else {
             handleEmtpy();
         }
@@ -2087,7 +2091,13 @@ public class ShowExecutor {
         }
         resultSet = new ShowResultSet(showStmt.getMetaData(), results);
     }
-}
-
 
+    public void handleShowSqlBlockRule() throws AnalysisException {
+        ShowSqlBlockRuleStmt showStmt = (ShowSqlBlockRuleStmt) stmt;
+        List<List<String>> rows = Lists.newArrayList();
+        List<SqlBlockRule> sqlBlockRules = 
Catalog.getCurrentCatalog().getSqlBlockRuleMgr().getSqlBlockRule(showStmt);
+        sqlBlockRules.forEach(rule -> rows.add(rule.getShowInfo()));
+        resultSet = new ShowResultSet(showStmt.getMetaData(), rows);
+    }
 
+}
diff --git a/fe/fe-core/src/main/jflex/sql_scanner.flex 
b/fe/fe-core/src/main/jflex/sql_scanner.flex
index 5e99627..3fa6fb6 100644
--- a/fe/fe-core/src/main/jflex/sql_scanner.flex
+++ b/fe/fe-core/src/main/jflex/sql_scanner.flex
@@ -408,6 +408,7 @@ import org.apache.doris.qe.SqlModeHelper;
         keywordMap.put("write", new Integer(SqlParserSymbols.KW_WRITE));
         keywordMap.put("year", new Integer(SqlParserSymbols.KW_YEAR));
         keywordMap.put("||", new Integer(SqlParserSymbols.KW_PIPE));
+        keywordMap.put("sql_block_rule", new 
Integer(SqlParserSymbols.KW_SQL_BLOCK_RULE));
    }
     
   // map from token id to token description
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/analysis/AlterSqlBlockRuleStmtTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/analysis/AlterSqlBlockRuleStmtTest.java
new file mode 100644
index 0000000..1ed1417
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/analysis/AlterSqlBlockRuleStmtTest.java
@@ -0,0 +1,72 @@
+// 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.doris.analysis;
+
+import org.apache.doris.blockrule.SqlBlockRule;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.UserException;
+import org.apache.doris.mysql.privilege.MockedAuth;
+import org.apache.doris.mysql.privilege.PaloAuth;
+import org.apache.doris.qe.ConnectContext;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import mockit.Mocked;
+
+public class AlterSqlBlockRuleStmtTest {
+
+    private Analyzer analyzer;
+
+    @Mocked
+    private PaloAuth auth;
+
+    @Mocked
+    private ConnectContext ctx;
+
+    @Before
+    public void setUp() {
+        analyzer = AccessTestUtil.fetchAdminAnalyzer(true);
+        MockedAuth.mockedAuth(auth);
+        MockedAuth.mockedConnectContext(ctx, "root", "192.168.1.1");
+    }
+
+    @Test
+    public void testNormal() throws UserException {
+        Map<String, String> properties = new HashMap<>();
+        properties.put(CreateSqlBlockRuleStmt.SQL_PROPERTY, "select \\* from 
test_table");
+        properties.put(CreateSqlBlockRuleStmt.ENABLE_PROPERTY, "false");
+        AlterSqlBlockRuleStmt stmt = new AlterSqlBlockRuleStmt("test_rule", 
properties);
+        stmt.analyze(analyzer);
+        SqlBlockRule rule = SqlBlockRule.fromAlterStmt(stmt);
+        Assert.assertEquals(false, rule.getEnable());
+        Assert.assertEquals("select \\* from test_table", rule.getSql());
+        Assert.assertEquals("test_rule", rule.getName());
+    }
+
+    @Test(expected = AnalysisException.class)
+    public void testNoProps() throws UserException {
+        Map<String, String> properties = new HashMap<>();
+        AlterSqlBlockRuleStmt stmt = new AlterSqlBlockRuleStmt("test_rule", 
properties);
+        stmt.analyze(analyzer);
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/analysis/CreateSqlBlockRuleStmtTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/analysis/CreateSqlBlockRuleStmtTest.java
new file mode 100644
index 0000000..f2cb284
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/analysis/CreateSqlBlockRuleStmtTest.java
@@ -0,0 +1,73 @@
+// 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.doris.analysis;
+
+import org.apache.doris.blockrule.SqlBlockRule;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.UserException;
+import org.apache.doris.mysql.privilege.MockedAuth;
+import org.apache.doris.mysql.privilege.PaloAuth;
+import org.apache.doris.qe.ConnectContext;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import mockit.Mocked;
+
+public class CreateSqlBlockRuleStmtTest {
+
+    private Analyzer analyzer;
+
+    @Mocked
+    private PaloAuth auth;
+
+    @Mocked
+    private ConnectContext ctx;
+
+    @Before
+    public void setUp() {
+        analyzer = AccessTestUtil.fetchAdminAnalyzer(true);
+        MockedAuth.mockedAuth(auth);
+        MockedAuth.mockedConnectContext(ctx, "root", "192.168.1.1");
+    }
+
+    @Test
+    public void testNormal() throws UserException {
+        Map<String, String> properties = new HashMap<>();
+        properties.put(CreateSqlBlockRuleStmt.SQL_PROPERTY, "select \\* from 
test_table");
+        properties.put(CreateSqlBlockRuleStmt.ENABLE_PROPERTY, "true");
+        CreateSqlBlockRuleStmt stmt = new CreateSqlBlockRuleStmt("test_rule", 
properties);
+        stmt.analyze(analyzer);
+        SqlBlockRule rule = SqlBlockRule.fromCreateStmt(stmt);
+        Assert.assertEquals(true, rule.getEnable());
+        Assert.assertEquals("select \\* from test_table", rule.getSql());
+        Assert.assertEquals("test_rule", rule.getName());
+    }
+
+    @Test(expected = AnalysisException.class)
+    public void testNoProps() throws UserException {
+        Map<String, String> properties = new HashMap<>();
+        properties.put(CreateSqlBlockRuleStmt.ENABLE_PROPERTY, "true");
+        CreateSqlBlockRuleStmt stmt = new CreateSqlBlockRuleStmt("test_rule", 
properties);
+        stmt.analyze(analyzer);
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/blockrule/SqlBlockRuleMgrTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/blockrule/SqlBlockRuleMgrTest.java
new file mode 100644
index 0000000..8fd7af2
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/blockrule/SqlBlockRuleMgrTest.java
@@ -0,0 +1,129 @@
+// 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.doris.blockrule;
+
+import org.apache.doris.analysis.CreateDbStmt;
+import org.apache.doris.analysis.CreateSqlBlockRuleStmt;
+import org.apache.doris.analysis.CreateTableStmt;
+import org.apache.doris.analysis.SetUserPropertyStmt;
+import org.apache.doris.catalog.Catalog;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.ExceptionChecker;
+import org.apache.doris.metric.MetricRepo;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.utframe.UtFrameUtils;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.UUID;
+
+public class SqlBlockRuleMgrTest {
+    
+    private static String runningDir = "fe/mocked/SqlBlockRuleMgrTest/" + 
UUID.randomUUID().toString() + "/";
+    
+    private static ConnectContext connectContext;
+    
+    @BeforeClass
+    public static void beforeClass() throws Exception {
+        UtFrameUtils.createMinDorisCluster(runningDir);
+        
+        // create connect context
+        connectContext = UtFrameUtils.createDefaultCtx();
+        
+        // create database
+        String createDbStmtStr = "create database test;";
+        CreateDbStmt createDbStmt = (CreateDbStmt) 
UtFrameUtils.parseAndAnalyzeStmt(createDbStmtStr, connectContext);
+        Catalog.getCurrentCatalog().createDb(createDbStmt);
+        
+        MetricRepo.init();
+        createTable("create table test.table1\n" +
+                "(k1 int, k2 int) distributed by hash(k1) buckets 1\n" +
+                "properties(\"replication_num\" = \"1\");");
+        
+    }
+    
+    @AfterClass
+    public static void tearDown() {
+        File file = new File(runningDir);
+        file.delete();
+    }
+    
+    private static void createTable(String sql) throws Exception {
+        CreateTableStmt createTableStmt = (CreateTableStmt) 
UtFrameUtils.parseAndAnalyzeStmt(sql, connectContext);
+        Catalog.getCurrentCatalog().createTable(createTableStmt);
+    }
+    
+    @Test
+    public void testUserMatchSql() throws Exception {
+        String sql = "select * from table1 limit 10";
+        String sqlHash = DigestUtils.md5Hex(sql);
+        SqlBlockRule sqlRule = new SqlBlockRule("test_rule1", null, sqlHash, 
false, true);
+        SqlBlockRuleMgr mgr = new SqlBlockRuleMgr();
+        mgr.replayCreate(sqlRule);
+        // sql block rules
+        String setPropertyStr = "set property for \"root\" \"sql_block_rules\" 
= \"test_rule1\"";
+        SetUserPropertyStmt setUserPropertyStmt = (SetUserPropertyStmt) 
UtFrameUtils.parseAndAnalyzeStmt(setPropertyStr, connectContext);
+        
Catalog.getCurrentCatalog().getAuth().updateUserProperty(setUserPropertyStmt);
+        ExceptionChecker.expectThrowsWithMsg(AnalysisException.class, "sql 
match hash sql block rule: " + sqlRule.getName(),
+                () -> mgr.matchSql(sql, sqlHash, "root"));
+    }
+    
+    @Test
+    public void testGlobalMatchSql() throws AnalysisException {
+        String sql = "select * from test_table1 limit 10";
+        String sqlHash = DigestUtils.md5Hex(sql);
+        SqlBlockRule sqlRule = new SqlBlockRule("test_rule1", null, sqlHash, 
true, true);
+        SqlBlockRuleMgr mgr = new SqlBlockRuleMgr();
+        mgr.replayCreate(sqlRule);
+        ExceptionChecker.expectThrowsWithMsg(AnalysisException.class, "sql 
match hash sql block rule: " + sqlRule.getName(),
+                () -> mgr.matchSql(sql, sqlHash, "test"));
+    }
+    
+    @Test
+    public void testRegexMatchSql() throws AnalysisException {
+        String sql = "select * from test_table1 tt1 join test_table2 tt2 on 
tt1.testId=tt2.testId limit 5";
+        String sqlHash = DigestUtils.md5Hex(sql);
+        SqlBlockRule sqlRule = new SqlBlockRule("test_rule1", ".* join .*", 
null, true, true);
+        SqlBlockRuleMgr mgr = new SqlBlockRuleMgr();
+        mgr.replayCreate(sqlRule);
+        ExceptionChecker.expectThrowsWithMsg(AnalysisException.class, "sql 
match regex sql block rule: " + sqlRule.getName(),
+                () -> mgr.matchSql(sqlRule, sql, sqlHash));
+    }
+    
+    @Test
+    public void testHashMatchSql() throws AnalysisException {
+        String sql = "select * from test_table1 tt1 join test_table2 tt2 on 
tt1.testId=tt2.testId limit 5";
+        String sqlHash = DigestUtils.md5Hex(sql);
+        System.out.println(sqlHash);
+        SqlBlockRule sqlRule = new SqlBlockRule("test_rule1", null, sqlHash, 
true, true);
+        SqlBlockRuleMgr mgr = new SqlBlockRuleMgr();
+        mgr.replayCreate(sqlRule);
+        ExceptionChecker.expectThrowsWithMsg(AnalysisException.class, "sql 
match hash sql block rule: " + sqlRule.getName(),
+                () -> mgr.matchSql(sqlRule, sql, sqlHash));
+    }
+    
+    @Test
+    public void testNormalCreate() throws Exception {
+        String createSql = "CREATE SQL_BLOCK_RULE test_rule 
PROPERTIES(\"sql\"=\"select \\\\* from test_table\",\"enable\"=\"true\")";
+        CreateSqlBlockRuleStmt createSqlBlockRuleStmt = 
(CreateSqlBlockRuleStmt) UtFrameUtils.parseAndAnalyzeStmt(createSql, 
connectContext);
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/catalog/UserPropertyTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/catalog/UserPropertyTest.java
index 6fd1bf7..a98049d 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/catalog/UserPropertyTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/catalog/UserPropertyTest.java
@@ -113,5 +113,15 @@ public class UserPropertyTest {
         userProperty.update(properties);
         Assert.assertEquals(null, 
userProperty.getLoadClusterInfo("dpp-cluster").second);
         Assert.assertEquals(null, userProperty.getDefaultLoadCluster());
+
+        // sql block rule
+        properties.clear();
+        properties.add(Pair.create("sql_block_rules", ""));
+        userProperty.update(properties);
+        Assert.assertEquals(1, userProperty.getSqlBlockRules().length);
+        properties.clear();
+        properties.add(Pair.create("sql_block_rules", "test1, test2,test3"));
+        userProperty.update(properties);
+        Assert.assertEquals(3, userProperty.getSqlBlockRules().length);
     }
 }
diff --git a/fe/fe-core/src/test/java/org/apache/doris/qe/ShowExecutorTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/qe/ShowExecutorTest.java
index a958bb5..bbb7110 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/qe/ShowExecutorTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/qe/ShowExecutorTest.java
@@ -29,6 +29,7 @@ import org.apache.doris.analysis.ShowCreateTableStmt;
 import org.apache.doris.analysis.ShowDbStmt;
 import org.apache.doris.analysis.ShowEnginesStmt;
 import org.apache.doris.analysis.ShowProcedureStmt;
+import org.apache.doris.analysis.ShowSqlBlockRuleStmt;
 import org.apache.doris.analysis.ShowTableStmt;
 import org.apache.doris.analysis.ShowVariablesStmt;
 import org.apache.doris.analysis.ShowViewStmt;
@@ -589,4 +590,17 @@ public class ShowExecutorTest {
 
         Assert.assertFalse(resultSet.next());
     }
+
+    @Test
+    public void testShowSqlBlockRule() throws AnalysisException {
+        ShowSqlBlockRuleStmt stmt = new ShowSqlBlockRuleStmt("test_rule");
+        ShowExecutor executor = new ShowExecutor(ctx, stmt);
+        ShowResultSet resultSet = executor.execute();
+        Assert.assertEquals(5, resultSet.getMetaData().getColumnCount());
+        Assert.assertEquals("Name", 
resultSet.getMetaData().getColumn(0).getName());
+        Assert.assertEquals("Sql", 
resultSet.getMetaData().getColumn(1).getName());
+        Assert.assertEquals("SqlHash", 
resultSet.getMetaData().getColumn(2).getName());
+        Assert.assertEquals("Global", 
resultSet.getMetaData().getColumn(3).getName());
+        Assert.assertEquals("Enable", 
resultSet.getMetaData().getColumn(4).getName());
+    }
 }

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to