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]