[feature-wip](multi-catalog)Support use catalog.db and show databases from catalog stmt (#11338)

Support use catalog.db and show databases from catalog stmt.
This commit is contained in:
huangzhaowei
2022-08-11 09:50:32 +08:00
committed by GitHub
parent 5d99abb3ec
commit 04d26ddf22
15 changed files with 316 additions and 12 deletions

View File

@ -37,10 +37,59 @@ This statement is used to display the currently visible db
grammar:
```sql
SHOW DATABASES;
SHOW DATABASES [FROM catalog] [filter expr];
````
illustrate:
1. `SHOW DATABASES` will get all database names from current catalog.
2. `SHOW DATABASES FROM catalog` will all database names from the catalog named 'catalog'.
3. `SHOW DATABASES filter_expr` will get filtered database names from current catalog.
4. `SHOW DATABASES FROM catalog filter_expr` is not support yet.
### Example
1. Display all the database names from current catalog.
```sql
SHOW DATABASES;
````
````
+--------------------+
| Database |
+--------------------+
| test |
| information_schema |
+--------------------+
````
2. Display all database names from the catalog named 'hms_catalog'.
```sql
SHOW DATABASES from hms_catalog;
````
````
+---------------+
| Database |
+---------------+
| default |
| tpch |
+---------------+
````
3. Display the filtered database names from current catalog with the expr 'like'.
```sql
SHOW DATABASES like 'infor%';
````
````
+--------------------+
| Database |
+--------------------+
| information_schema |
+--------------------+
````
### Keywords

View File

@ -37,17 +37,26 @@ The USE command allows us to use the database
grammar:
````SQL
USE <DATABASE_NAME>
USE <[CATALOG_NAME].DATABASE_NAME>
````
illustrate:
1. `USE CATALOG_NAME.DATABASE_NAME` will switch the current catalog into `CATALOG_NAME` and then change the current database into `DATABASE_NAME`
### Example
1. If the demo database exists, try accessing it:
1. If the demo database exists in current catalog, try accessing it:
```sql
mysql> use demo;
Database changed
````
2. If the demo database exists in catalog hms_catalog, try switching the catalog and accessing it:
```sql
mysql> use hms_catalog.demo;
Database changed
````
### Keywords

View File

@ -37,10 +37,59 @@ SHOW DATABASES
语法:
```sql
SHOW DATABASES;
```
SHOW DATABASES [FROM catalog] [filter expr];
````
说明:
1. `SHOW DATABASES` 会展示当前所有的数据库名称.
2. `SHOW DATABASES FROM catalog` 会展示`catalog`中所有的数据库名称.
3. `SHOW DATABASES filter_expr` 会展示当前所有经过过滤后的数据库名称.
4. `SHOW DATABASES FROM catalog filter_expr` 这种语法不支持.
### Example
1. 展示当前所有的数据库名称.
```sql
SHOW DATABASES;
````
````
+--------------------+
| Database |
+--------------------+
| test |
| information_schema |
+--------------------+
````
2. 会展示`hms_catalog`中所有的数据库名称.
```sql
SHOW DATABASES from hms_catalog;
````
````
+---------------+
| Database |
+---------------+
| default |
| tpch |
+---------------+
````
3. 展示当前所有经过表示式`like 'infor%'`过滤后的数据库名称.
```sql
SHOW DATABASES like 'infor%';
````
````
+--------------------+
| Database |
+--------------------+
| information_schema |
+--------------------+
````
### Keywords

View File

@ -37,18 +37,27 @@ USE 命令可以让我们来使用数据库
语法:
```SQL
USE <DATABASE_NAME>
USE <[CATALOG_NAME].DATABASE_NAME>
```
说明:
1. 使用`USE CATALOG_NAME.DATABASE_NAME`, 会先将当前的Catalog切换为`CATALOG_NAME`, 然后再讲当前的Database切换为`DATABASE_NAME`
### Example
1. 如果 demo 数据库存在,尝试存取它:
1. 如果 demo 数据库存在,尝试使用它:
```sql
mysql> use demo;
Database changed
```
2. 如果 demo 数据库在hms_catalog的Catalog下存在,尝试切换到hms_catalog, 并使用它:
```sql
mysql> use hms_catalog.demo;
Database changed
````
### Keywords
USE

View File

@ -2842,6 +2842,10 @@ show_param ::=
{:
RESULT = new ShowDbStmt(parser.wild, parser.where);
:}
| KW_DATABASES KW_FROM ident:catalogName
{:
RESULT = new ShowDbStmt(null, null, catalogName);
:}
/* show database id */
| KW_DATABASE INTEGER_LITERAL:dbId
{:
@ -2851,6 +2855,10 @@ show_param ::=
{:
RESULT = new ShowDbStmt(parser.wild, parser.where);
:}
| KW_SCHEMAS KW_FROM ident:catalogName
{:
RESULT = new ShowDbStmt(null, null, catalogName);
:}
/* Catalog */
| KW_CATALOGS
{:
@ -3569,6 +3577,10 @@ use_stmt ::=
{:
RESULT = new UseStmt(db);
:}
| KW_USE ident:ctl DOT ident:db
{:
RESULT = new UseStmt(ctl, db);
:}
;
// Insert statement

View File

@ -38,6 +38,7 @@ public class ShowDbStmt extends ShowStmt {
.build();
private String pattern;
private String catalogName;
private Expr where;
private SelectStmt selectStmt;
@ -50,10 +51,20 @@ public class ShowDbStmt extends ShowStmt {
this.where = where;
}
public ShowDbStmt(String pattern, Expr where, String catalogName) {
this.pattern = pattern;
this.where = where;
this.catalogName = catalogName;
}
public String getPattern() {
return pattern;
}
public String getCatalogName() {
return catalogName;
}
@Override
public void analyze(Analyzer analyzer) throws AnalysisException, UserException {
super.analyze(analyzer);
@ -87,6 +98,9 @@ public class ShowDbStmt extends ShowStmt {
if (pattern != null) {
sb.append(" LIKE '").append(pattern).append("'");
}
if (catalogName != null) {
sb.append(" FROM ").append(catalogName);
}
return sb.toString();
}

View File

@ -35,19 +35,35 @@ import org.apache.logging.log4j.Logger;
*/
public class UseStmt extends StatementBase {
private static final Logger LOG = LogManager.getLogger(UseStmt.class);
private String catalogName;
private String database;
public UseStmt(String db) {
database = db;
}
public UseStmt(String catalogName, String db) {
this.catalogName = catalogName;
this.database = db;
}
public String getDatabase() {
return database;
}
public String getCatalogName() {
return catalogName;
}
@Override
public String toSql() {
return "USE `" + database + "`";
StringBuilder sb = new StringBuilder();
sb.append("USE ");
if (catalogName != null) {
sb.append("`").append(catalogName).append("`.");
}
sb.append("`").append(database).append("`");
return sb.toString();
}
@Override

View File

@ -425,10 +425,18 @@ public class ConnectContext {
public DataSourceIf getCurrentDataSource() {
// defaultCatalog is switched by SwitchStmt, so we don't need to check to exist of catalog.
return getDataSource(defaultCatalog);
}
/**
* Maybe return when catalogName is not exist. So need to check nullable.
*/
public DataSourceIf getDataSource(String catalogName) {
String realCatalogName = catalogName == null ? defaultCatalog : catalogName;
if (env == null) {
return Env.getCurrentEnv().getDataSourceMgr().getCatalog(defaultCatalog);
return Env.getCurrentEnv().getDataSourceMgr().getCatalog(realCatalogName);
}
return env.getDataSourceMgr().getCatalog(defaultCatalog);
return env.getDataSourceMgr().getCatalog(realCatalogName);
}
public void changeDefaultCatalog(String catalogName) {

View File

@ -39,6 +39,7 @@ import org.apache.doris.common.Pair;
import org.apache.doris.common.UserException;
import org.apache.doris.common.util.DebugUtil;
import org.apache.doris.common.util.SqlParserUtils;
import org.apache.doris.datasource.DataSourceIf;
import org.apache.doris.metric.MetricRepo;
import org.apache.doris.mysql.MysqlChannel;
import org.apache.doris.mysql.MysqlCommand;
@ -89,13 +90,41 @@ public class ConnectProcessor {
// COM_INIT_DB: change current database of this session.
private void handleInitDb() {
String dbName = new String(packetBuf.array(), 1, packetBuf.limit() - 1);
String fullDbName = new String(packetBuf.array(), 1, packetBuf.limit() - 1);
if (Strings.isNullOrEmpty(ctx.getClusterName())) {
ctx.getState().setError(ErrorCode.ERR_CLUSTER_NAME_NULL, "Please enter cluster");
return;
}
String catalogName = null;
String dbName = null;
String[] dbNames = fullDbName.split("\\.");
if (dbNames.length == 1) {
dbName = fullDbName;
} else if (dbNames.length == 2) {
catalogName = dbNames[0];
dbName = dbNames[1];
} else if (dbNames.length > 2) {
ctx.getState().setError(ErrorCode.ERR_BAD_DB_ERROR, "Only one dot can be in the name: " + fullDbName);
return;
}
dbName = ClusterNamespace.getFullName(ctx.getClusterName(), dbName);
// check catalog and db exists
if (catalogName != null) {
DataSourceIf dataSourceIf = ctx.getEnv().getDataSourceMgr().getCatalogNullable(catalogName);
if (dataSourceIf == null) {
ctx.getState().setError(ErrorCode.ERR_BAD_DB_ERROR, "No match catalog in doris: " + fullDbName);
return;
}
if (dataSourceIf.getDbNullable(dbName) == null) {
ctx.getState().setError(ErrorCode.ERR_BAD_DB_ERROR, "No match database in doris: " + fullDbName);
return;
}
}
try {
if (catalogName != null) {
ctx.getEnv().changeCatalog(ctx, catalogName);
}
ctx.getEnv().changeDb(ctx, dbName);
} catch (DdlException e) {
ctx.getState().setError(e.getMysqlErrorCode(), e.getMessage());

View File

@ -156,6 +156,7 @@ import org.apache.doris.common.util.ProfileManager;
import org.apache.doris.common.util.RuntimeProfile;
import org.apache.doris.common.util.TimeUtils;
import org.apache.doris.common.util.Util;
import org.apache.doris.datasource.DataSourceIf;
import org.apache.doris.external.iceberg.IcebergTableCreationRecord;
import org.apache.doris.load.DeleteHandler;
import org.apache.doris.load.ExportJob;
@ -658,7 +659,11 @@ public class ShowExecutor {
ShowDbStmt showDbStmt = (ShowDbStmt) stmt;
List<List<String>> rows = Lists.newArrayList();
// cluster feature is deprecated.
List<String> dbNames = ctx.getCurrentDataSource().getDbNames();
DataSourceIf dataSourceIf = ctx.getDataSource(showDbStmt.getCatalogName());
if (dataSourceIf == null) {
throw new AnalysisException("No catalog found with name " + showDbStmt.getCatalogName());
}
List<String> dbNames = dataSourceIf.getDbNames();
PatternMatcher matcher = null;
if (showDbStmt.getPattern() != null) {
matcher = PatternMatcher.createMysqlPattern(showDbStmt.getPattern(),

View File

@ -1494,6 +1494,9 @@ public class StmtExecutor implements ProfileWriter {
if (Strings.isNullOrEmpty(useStmt.getClusterName())) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_CLUSTER_NO_SELECT_CLUSTER);
}
if (useStmt.getCatalogName() != null) {
context.getEnv().changeCatalog(context, useStmt.getCatalogName());
}
context.getEnv().changeDb(context, useStmt.getDatabase());
} catch (DdlException e) {
context.getState().setError(e.getMysqlErrorCode(), e.getMessage());

View File

@ -40,5 +40,13 @@ public class ShowDbStmtTest {
Assert.assertEquals("SHOW DATABASES LIKE 'abc'", stmt.toString());
Assert.assertEquals(1, stmt.getMetaData().getColumnCount());
Assert.assertEquals("Database", stmt.getMetaData().getColumn(0).getName());
stmt = new ShowDbStmt(null, null, "cn");
stmt.analyze(analyzer);
Assert.assertEquals("cn", stmt.getCatalogName());
Assert.assertNull(stmt.getPattern());
Assert.assertEquals("SHOW DATABASES FROM cn", stmt.toString());
Assert.assertEquals(1, stmt.getMetaData().getColumnCount());
Assert.assertEquals("Database", stmt.getMetaData().getColumn(0).getName());
}
}

View File

@ -59,4 +59,14 @@ public class UseStmtTest {
Assert.fail("No exception throws.");
}
@Test
public void testFromCatalog() throws UserException, AnalysisException {
UseStmt stmt = new UseStmt("cn", "testDb");
stmt.analyze(analyzer);
Assert.assertEquals("USE `cn`.`testCluster:testDb`", stmt.toString());
Assert.assertEquals("testCluster:testDb", stmt.getDatabase());
Assert.assertEquals("cn", stmt.getCatalogName());
}
}

View File

@ -289,6 +289,16 @@ public class ShowExecutorTest {
Assert.assertFalse(resultSet.next());
}
@Test
public void testShowDbFromCatalog() throws AnalysisException {
ShowDbStmt stmt = new ShowDbStmt(null, null, InternalDataSource.INTERNAL_DS_NAME);
ShowExecutor executor = new ShowExecutor(ctx, stmt);
ShowResultSet resultSet = executor.execute();
Assert.assertTrue(resultSet.next());
Assert.assertEquals("testDb", resultSet.getString(0));
}
@Test
public void testShowDbPriv() throws AnalysisException {
ShowDbStmt stmt = new ShowDbStmt(null);

View File

@ -34,6 +34,7 @@ import org.apache.doris.catalog.Env;
import org.apache.doris.common.DdlException;
import org.apache.doris.common.jmockit.Deencapsulation;
import org.apache.doris.common.util.RuntimeProfile;
import org.apache.doris.datasource.InternalDataSource;
import org.apache.doris.metric.MetricRepo;
import org.apache.doris.mysql.MysqlChannel;
import org.apache.doris.mysql.MysqlSerializer;
@ -736,4 +737,76 @@ public class StmtExecutorTest {
Assert.assertEquals(QueryState.MysqlStateType.ERR, state.getStateType());
}
@Test
public void testUseWithCatalog(@Mocked UseStmt useStmt, @Mocked SqlParser parser) throws Exception {
new Expectations() {
{
useStmt.analyze((Analyzer) any);
minTimes = 0;
useStmt.getDatabase();
minTimes = 0;
result = "testCluster:testDb";
useStmt.getRedirectStatus();
minTimes = 0;
result = RedirectStatus.NO_FORWARD;
useStmt.getClusterName();
minTimes = 0;
result = "testCluster";
useStmt.getCatalogName();
minTimes = 0;
result = InternalDataSource.INTERNAL_DS_NAME;
Symbol symbol = new Symbol(0, Lists.newArrayList(useStmt));
parser.parse();
minTimes = 0;
result = symbol;
}
};
StmtExecutor executor = new StmtExecutor(ctx, "");
executor.execute();
Assert.assertEquals(QueryState.MysqlStateType.OK, state.getStateType());
}
@Test
public void testUseWithCatalogFail(@Mocked UseStmt useStmt, @Mocked SqlParser parser) throws Exception {
new Expectations() {
{
useStmt.analyze((Analyzer) any);
minTimes = 0;
useStmt.getDatabase();
minTimes = 0;
result = "blockDb";
useStmt.getRedirectStatus();
minTimes = 0;
result = RedirectStatus.NO_FORWARD;
useStmt.getClusterName();
minTimes = 0;
result = "testCluster";
useStmt.getCatalogName();
minTimes = 0;
result = "testcatalog";
Symbol symbol = new Symbol(0, Lists.newArrayList(useStmt));
parser.parse();
minTimes = 0;
result = symbol;
}
};
StmtExecutor executor = new StmtExecutor(ctx, "");
executor.execute();
Assert.assertEquals(QueryState.MysqlStateType.ERR, state.getStateType());
}
}