[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:
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user