This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 34813bae13 [improvement](meta) make database,table,column names to
support unicode (replace PR #13467 with this) (#14531)
34813bae13 is described below
commit 34813bae137ca856c5ece8d60b85f97817226c4e
Author: lvliang <[email protected]>
AuthorDate: Tue Feb 28 18:50:36 2023 +0800
[improvement](meta) make database,table,column names to support unicode
(replace PR #13467 with this) (#14531)
Make database, table, column and other names support unicode by changing
LABEL_REGEX COMMON_NAME_REGIEX COMMON_TABLE_NAME_REGEX COLUMN_NAME_REGEX
regular expressions in class FeNameFormat.
P.S. @SharpRay has transfered PR #13467 to me, and I‘m responsible for the
task now. There will be some modifications during the review period, so I
create a new PR and the original #13467 could be closed. Thanks.
---
be/src/http/action/stream_load.cpp | 5 +-
.../java/org/apache/doris/common/FeNameFormat.java | 76 +++++++++++++++++-----
.../java/org/apache/doris/qe/SessionVariable.java | 15 +++++
.../apache/doris/analysis/CreateViewStmtTest.java | 3 +-
.../org/apache/doris/common/FeNameFormatTest.java | 10 +++
.../apache/doris/ldap/LdapPrivsCheckerTest.java | 11 ++++
regression-test/data/ddl_p0/test_unicode_name.out | 4 ++
.../suites/ddl_p0/test_unicode_name.groovy | 53 +++++++++++++++
8 files changed, 159 insertions(+), 18 deletions(-)
diff --git a/be/src/http/action/stream_load.cpp
b/be/src/http/action/stream_load.cpp
index 7340e6c10c..c65e554a8b 100644
--- a/be/src/http/action/stream_load.cpp
+++ b/be/src/http/action/stream_load.cpp
@@ -60,6 +60,7 @@
#include "util/thrift_rpc_helper.h"
#include "util/time.h"
#include "util/uid_util.h"
+#include "util/url_coding.h"
namespace doris {
using namespace ErrorCode;
@@ -227,8 +228,8 @@ int StreamLoadAction::on_header(HttpRequest* req) {
ctx->load_type = TLoadType::MANUL_LOAD;
ctx->load_src_type = TLoadSourceType::RAW;
- ctx->db = req->param(HTTP_DB_KEY);
- ctx->table = req->param(HTTP_TABLE_KEY);
+ url_decode(req->param(HTTP_DB_KEY), &ctx->db);
+ url_decode(req->param(HTTP_TABLE_KEY), &ctx->table);
ctx->label = req->header(HTTP_LABEL_KEY);
if (ctx->label.empty()) {
ctx->label = generate_uuid_string();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/FeNameFormat.java
b/fe/fe-core/src/main/java/org/apache/doris/common/FeNameFormat.java
index 2a2c61abd0..428f793ce7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/FeNameFormat.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/FeNameFormat.java
@@ -21,35 +21,43 @@ import org.apache.doris.alter.SchemaChangeHandler;
import org.apache.doris.analysis.CreateMaterializedViewStmt;
import org.apache.doris.datasource.InternalCatalog;
import org.apache.doris.mysql.privilege.Role;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.VariableMgr;
import com.google.common.base.Strings;
public class FeNameFormat {
private static final String LABEL_REGEX = "^[-_A-Za-z0-9]{1,128}$";
private static final String COMMON_NAME_REGEX =
"^[a-zA-Z][a-zA-Z0-9_]{0,63}$";
- private static final String COMMON_TABLE_NAME_REGEX =
"^[a-zA-Z][a-zA-Z0-9_]*$";
+ private static final String TABLE_NAME_REGEX = "^[a-zA-Z][a-zA-Z0-9_]*$";
private static final String COLUMN_NAME_REGEX =
"^[_a-zA-Z@0-9][.a-zA-Z0-9_+-/><?@#$%^&*]{0,255}$";
+ private static final String UNICODE_LABEL_REGEX =
"^[-_A-Za-z0-9\\p{L}]{1,128}$";
+ private static final String UNICODE_COMMON_NAME_REGEX =
"^[a-zA-Z\\p{L}][a-zA-Z0-9_\\p{L}]{0,63}$";
+ private static final String UNICODE_TABLE_NAME_REGEX =
"^[a-zA-Z\\p{L}][a-zA-Z0-9_\\p{L}]*$";
+ private static final String UNICODE_COLUMN_NAME_REGEX
+ = "^[_a-zA-Z@0-9\\p{L}][.a-zA-Z0-9_+-/><?@#$%^&*\\p{L}]{0,255}$";
+
public static final String FORBIDDEN_PARTITION_NAME = "placeholder_";
public static void checkCatalogName(String catalogName) throws
AnalysisException {
if (!InternalCatalog.INTERNAL_CATALOG_NAME.equals(catalogName) &&
(Strings.isNullOrEmpty(catalogName)
- || !catalogName.matches(COMMON_NAME_REGEX))) {
+ || !catalogName.matches(getCommonNameRegex()))) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_CATALOG_NAME,
catalogName);
}
}
public static void checkDbName(String dbName) throws AnalysisException {
- if (Strings.isNullOrEmpty(dbName) ||
!dbName.matches(COMMON_NAME_REGEX)) {
+ if (Strings.isNullOrEmpty(dbName) ||
!dbName.matches(getCommonNameRegex())) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_DB_NAME,
dbName);
}
}
public static void checkTableName(String tableName) throws
AnalysisException {
if (Strings.isNullOrEmpty(tableName)
- || !tableName.matches(COMMON_TABLE_NAME_REGEX)) {
+ || !tableName.matches(getTableNameRegex())) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_TABLE_NAME, tableName,
- COMMON_TABLE_NAME_REGEX);
+ getTableNameRegex());
}
if (tableName.length() > Config.table_name_length_limit) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_TABLE_NAME_LENGTH_LIMIT,
tableName,
@@ -58,7 +66,7 @@ public class FeNameFormat {
}
public static void checkPartitionName(String partitionName) throws
AnalysisException {
- if (Strings.isNullOrEmpty(partitionName) ||
!partitionName.matches(COMMON_NAME_REGEX)) {
+ if (Strings.isNullOrEmpty(partitionName) ||
!partitionName.matches(getCommonNameRegex())) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_PARTITION_NAME,
partitionName);
}
@@ -68,13 +76,13 @@ public class FeNameFormat {
}
public static void checkColumnName(String columnName) throws
AnalysisException {
- if (Strings.isNullOrEmpty(columnName) ||
!columnName.matches(COLUMN_NAME_REGEX)) {
+ if (Strings.isNullOrEmpty(columnName) ||
!columnName.matches(getColumnNameRegex())) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_COLUMN_NAME,
- columnName, FeNameFormat.COLUMN_NAME_REGEX);
+ columnName, getColumnNameRegex());
}
if (columnName.startsWith(SchemaChangeHandler.SHADOW_NAME_PREFIX)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_COLUMN_NAME,
- columnName, FeNameFormat.COLUMN_NAME_REGEX);
+ columnName, getColumnNameRegex());
}
if
(columnName.startsWith(CreateMaterializedViewStmt.MATERIALIZED_VIEW_NAME_PREFIX))
{
ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_COLUMN_NAME,
@@ -87,19 +95,19 @@ public class FeNameFormat {
}
public static void checkLabel(String label) throws AnalysisException {
- if (Strings.isNullOrEmpty(label) || !label.matches(LABEL_REGEX)) {
- throw new AnalysisException("Label format error. regex: " +
LABEL_REGEX + ", label: " + label);
+ if (Strings.isNullOrEmpty(label) || !label.matches(getLabelRegex())) {
+ throw new AnalysisException("Label format error. regex: " +
getLabelRegex() + ", label: " + label);
}
}
public static void checkUserName(String userName) throws AnalysisException
{
- if (Strings.isNullOrEmpty(userName) ||
!userName.matches(COMMON_NAME_REGEX)) {
+ if (Strings.isNullOrEmpty(userName) ||
!userName.matches(getCommonNameRegex())) {
throw new AnalysisException("invalid user name: " + userName);
}
}
public static void checkRoleName(String role, boolean canBeAdmin, String
errMsg) throws AnalysisException {
- if (Strings.isNullOrEmpty(role) || !role.matches(COMMON_NAME_REGEX)) {
+ if (Strings.isNullOrEmpty(role) ||
!role.matches(getCommonNameRegex())) {
throw new AnalysisException("invalid role format: " + role);
}
@@ -121,12 +129,50 @@ public class FeNameFormat {
}
public static void checkCommonName(String type, String name) throws
AnalysisException {
- if (Strings.isNullOrEmpty(name) || !name.matches(COMMON_NAME_REGEX)) {
+ if (Strings.isNullOrEmpty(name) ||
!name.matches(getCommonNameRegex())) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_NAME_FORMAT, type,
name);
}
}
+ private static boolean isEnableUnicodeNameSupport() {
+ boolean unicodeSupport;
+ if (ConnectContext.get() != null) {
+ unicodeSupport =
ConnectContext.get().getSessionVariable().isEnableUnicodeNameSupport();
+ } else {
+ unicodeSupport =
VariableMgr.getDefaultSessionVariable().isEnableUnicodeNameSupport();
+ }
+ return unicodeSupport;
+ }
+
public static String getColumnNameRegex() {
- return COLUMN_NAME_REGEX;
+ if (FeNameFormat.isEnableUnicodeNameSupport()) {
+ return UNICODE_COLUMN_NAME_REGEX;
+ } else {
+ return COLUMN_NAME_REGEX;
+ }
+ }
+
+ public static String getTableNameRegex() {
+ if (FeNameFormat.isEnableUnicodeNameSupport()) {
+ return UNICODE_TABLE_NAME_REGEX;
+ } else {
+ return TABLE_NAME_REGEX;
+ }
+ }
+
+ public static String getLabelRegex() {
+ if (FeNameFormat.isEnableUnicodeNameSupport()) {
+ return UNICODE_LABEL_REGEX;
+ } else {
+ return LABEL_REGEX;
+ }
+ }
+
+ public static String getCommonNameRegex() {
+ if (FeNameFormat.isEnableUnicodeNameSupport()) {
+ return UNICODE_COMMON_NAME_REGEX;
+ } else {
+ return COMMON_NAME_REGEX;
+ }
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
index cc11307e4a..1c9baee38b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
@@ -258,6 +258,9 @@ public class SessionVariable implements Serializable,
Writable {
public static final String ENABLE_SHARE_HASH_TABLE_FOR_BROADCAST_JOIN
= "enable_share_hash_table_for_broadcast_join";
+ // support unicode in label, table, column, common name check
+ public static final String ENABLE_UNICODE_NAME_SUPPORT =
"enable_unicode_name_support";
+
public static final String REPEAT_MAX_NUM = "repeat_max_num";
public static final String GROUP_CONCAT_MAX_LEN = "group_concat_max_len";
@@ -700,7 +703,11 @@ public class SessionVariable implements Serializable,
Writable {
@VariableMgr.VarAttr(name = ENABLE_SHARE_HASH_TABLE_FOR_BROADCAST_JOIN,
fuzzy = true)
public boolean enableShareHashTableForBroadcastJoin = true;
+ @VariableMgr.VarAttr(name = ENABLE_UNICODE_NAME_SUPPORT)
+ public boolean enableUnicodeNameSupport = false;
+
@VariableMgr.VarAttr(name = REPEAT_MAX_NUM, needForward = true)
+
public int repeatMaxNum = 10000;
@VariableMgr.VarAttr(name = GROUP_CONCAT_MAX_LEN)
@@ -1501,6 +1508,14 @@ public class SessionVariable implements Serializable,
Writable {
this.fragmentTransmissionCompressionCodec = codec;
}
+ public boolean isEnableUnicodeNameSupport() {
+ return enableUnicodeNameSupport;
+ }
+
+ public void setEnableUnicodeNameSupport(boolean enableUnicodeNameSupport) {
+ this.enableUnicodeNameSupport = enableUnicodeNameSupport;
+ }
+
public boolean isDropTableIfCtasFailed() {
return dropTableIfCtasFailed;
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/analysis/CreateViewStmtTest.java
b/fe/fe-core/src/test/java/org/apache/doris/analysis/CreateViewStmtTest.java
index 19d591f80c..3a80ee5b22 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/analysis/CreateViewStmtTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/CreateViewStmtTest.java
@@ -19,6 +19,7 @@ package org.apache.doris.analysis;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.ExceptionChecker;
+import org.apache.doris.common.FeNameFormat;
import org.apache.doris.utframe.TestWithFeService;
import org.junit.jupiter.api.Test;
@@ -42,7 +43,7 @@ public class CreateViewStmtTest extends TestWithFeService {
connectContext.setDatabase("default_cluster:test");
String createViewStr1 = "create view 1view1 as select k1,k2 from
test.table1;";
ExceptionChecker.expectThrowsWithMsg(AnalysisException.class,
- "Incorrect table name '1view1'. Table name regex is
'^[a-zA-Z][a-zA-Z0-9_]*$'",
+ String.format("Incorrect table name '1view1'. Table name regex
is '%s'", FeNameFormat.getTableNameRegex()),
() -> parseAndAnalyzeStmt(createViewStr1, connectContext));
String createViewStr2 = "create view view2 as select k1,k2 from
test.table1;";
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/common/FeNameFormatTest.java
b/fe/fe-core/src/test/java/org/apache/doris/common/FeNameFormatTest.java
index b952ab82e6..bd9777f915 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/common/FeNameFormatTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/common/FeNameFormatTest.java
@@ -23,6 +23,9 @@ public class FeNameFormatTest {
@Test
public void testCheckColumnName() {
+ // check label use correct regex, begin with '-' is different from
others
+ ExceptionChecker.expectThrowsNoException(() ->
FeNameFormat.checkLabel("-lable"));
+
ExceptionChecker.expectThrowsNoException(() ->
FeNameFormat.checkColumnName("_id"));
ExceptionChecker.expectThrowsNoException(() ->
FeNameFormat.checkColumnName("__id"));
ExceptionChecker.expectThrowsNoException(() ->
FeNameFormat.checkColumnName("___id"));
@@ -40,6 +43,13 @@ public class FeNameFormatTest {
// length 70
String largeTblName =
"test_sys_partition_list_basic_test_list_partition_bigint_tb_uniq_large";
ExceptionChecker.expectThrows(AnalysisException.class, () ->
FeNameFormat.checkTableName(largeTblName));
+
+ // check table name use correct regex, not begin with '-'
+ ExceptionChecker.expectThrows(AnalysisException.class, () ->
FeNameFormat.checkTableName("-" + tblName));
+
+ // check common name use correct regex, length 65
+ ExceptionChecker.expectThrows(AnalysisException.class, () ->
FeNameFormat.checkCommonName("fakeType", tblName + "t"));
+ ExceptionChecker.expectThrows(AnalysisException.class, () ->
FeNameFormat.checkCommonName("fakeType", "_commonName"));
}
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/ldap/LdapPrivsCheckerTest.java
b/fe/fe-core/src/test/java/org/apache/doris/ldap/LdapPrivsCheckerTest.java
index 3977e39a23..a39ef49eac 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/ldap/LdapPrivsCheckerTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/ldap/LdapPrivsCheckerTest.java
@@ -30,6 +30,7 @@ import org.apache.doris.mysql.privilege.PrivPredicate;
import org.apache.doris.mysql.privilege.Privilege;
import org.apache.doris.mysql.privilege.Role;
import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.SessionVariable;
import mockit.Expectations;
import mockit.Mocked;
@@ -55,6 +56,9 @@ public class LdapPrivsCheckerTest {
@Mocked
private ConnectContext context;
+ @Mocked
+ private SessionVariable sessionVariable;
+
@Mocked
private Env env;
@@ -128,8 +132,15 @@ public class LdapPrivsCheckerTest {
context.getCurrentUserIdentity();
minTimes = 0;
result = userIdentity;
+
+ context.getSessionVariable();
+ minTimes = 0;
+ result = sessionVariable;
}
};
+ // call the mocked method before replay
+ // for there is exception in tests: Missing 1 invocation to:
org.apache.doris.qe.ConnectContext#get()
+ ConnectContext.get().getSessionVariable().isEnableUnicodeNameSupport();
}
@Test
diff --git a/regression-test/data/ddl_p0/test_unicode_name.out
b/regression-test/data/ddl_p0/test_unicode_name.out
new file mode 100644
index 0000000000..e180f7fe9f
--- /dev/null
+++ b/regression-test/data/ddl_p0/test_unicode_name.out
@@ -0,0 +1,4 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !select --
+中文表名_1 CREATE TABLE `中文表名_1` (\n `字符串_字段名_test` varchar(150) NULL,\n
`时间_字段名_test` datetime NULL\n) ENGINE=OLAP\nUNIQUE KEY(`字符串_字段名_test`)\nCOMMENT
'OLAP'\nDISTRIBUTED BY HASH(`字符串_字段名_test`) BUCKETS 3\nPROPERTIES
(\n"replication_allocation" = "tag.location.default: 1",\n"in_memory" =
"false",\n"storage_format" = "V2",\n"light_schema_change" =
"true",\n"disable_auto_compaction" = "false"\n);
+
diff --git a/regression-test/suites/ddl_p0/test_unicode_name.groovy
b/regression-test/suites/ddl_p0/test_unicode_name.groovy
new file mode 100644
index 0000000000..5eb56e1c6b
--- /dev/null
+++ b/regression-test/suites/ddl_p0/test_unicode_name.groovy
@@ -0,0 +1,53 @@
+// 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.
+
+suite("test_unicode_name") {
+ try {
+ sql """
+ set enable_unicode_name_support = true
+ """
+ sql """
+ CREATE DATABASE IF NOT EXISTS `中文库名`
+ """
+
+ sql """
+ CREATE TABLE IF NOT EXISTS `中文表名` (
+ `字符串_字段名_test` varchar(150) NULL,
+ `时间_字段名_test` datetime NULL
+ ) ENGINE=OLAP
+ UNIQUE KEY(`字符串_字段名_test`)
+ DISTRIBUTED BY HASH(`字符串_字段名_test`) BUCKETS 3
+ PROPERTIES (
+ "replication_allocation" = "tag.location.default: 1",
+ "in_memory" = "false",
+ "storage_format" = "V2"
+ )
+ """
+
+ sql """
+ CREATE TABLE IF NOT EXISTS `中文表名_1` LIKE `中文表名`
+ """
+
+ qt_select """SHOW CREATE TABLE `中文表名_1`"""
+ } finally {
+ sql """ DROP DATABASE IF EXISTS `中文库名` """
+ sql """ DROP TABLE IF EXISTS `中文表名` """
+
+ sql """ DROP TABLE IF EXISTS `中文表名_1` """
+ }
+
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]