View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shardingsphere.proxy.backend.mysql.handler.admin;
19  
20  import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
21  import org.apache.shardingsphere.infra.binder.context.statement.type.dml.SelectStatementContext;
22  import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
23  import org.apache.shardingsphere.proxy.backend.handler.admin.executor.DatabaseAdminExecutor;
24  import org.apache.shardingsphere.proxy.backend.handler.admin.executor.DatabaseAdminExecutorCreator;
25  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.KillProcessExecutor;
26  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.MySQLSetVariableAdminExecutor;
27  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.MySQLSystemVariableQueryExecutor;
28  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.NoResourceShowExecutor;
29  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowConnectionIdExecutor;
30  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowCreateDatabaseExecutor;
31  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowCurrentDatabaseExecutor;
32  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowCurrentUserExecutor;
33  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowDatabasesExecutor;
34  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowFunctionStatusExecutor;
35  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowProcedureStatusExecutor;
36  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowProcessListExecutor;
37  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowTablesExecutor;
38  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowVersionExecutor;
39  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.UnicastResourceShowExecutor;
40  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.UseDatabaseExecutor;
41  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ExpressionProjectionSegment;
42  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ProjectionSegment;
43  import org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
44  import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dal.SetStatement;
45  import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.SelectStatement;
46  import org.apache.shardingsphere.sql.parser.statement.mysql.dal.MySQLKillStatement;
47  import org.apache.shardingsphere.sql.parser.statement.mysql.dal.MySQLUseStatement;
48  import org.apache.shardingsphere.sql.parser.statement.mysql.dal.show.database.MySQLShowCreateDatabaseStatement;
49  import org.apache.shardingsphere.sql.parser.statement.mysql.dal.show.database.MySQLShowDatabasesStatement;
50  import org.apache.shardingsphere.sql.parser.statement.mysql.dal.show.function.MySQLShowFunctionStatusStatement;
51  import org.apache.shardingsphere.sql.parser.statement.mysql.dal.show.procedure.MySQLShowProcedureStatusStatement;
52  import org.apache.shardingsphere.sql.parser.statement.mysql.dal.show.process.MySQLShowProcessListStatement;
53  import org.apache.shardingsphere.sql.parser.statement.mysql.dal.show.table.MySQLShowTablesStatement;
54  
55  import java.util.Collection;
56  import java.util.Iterator;
57  import java.util.List;
58  import java.util.Optional;
59  
60  /**
61   * Database admin executor creator for MySQL.
62   */
63  public final class MySQLAdminExecutorCreator implements DatabaseAdminExecutorCreator {
64      
65      private static final String INFORMATION_SCHEMA = "information_schema";
66      
67      private static final String MYSQL_SCHEMA = "mysql";
68      
69      private static final String PERFORMANCE_SCHEMA = "performance_schema";
70      
71      private static final String SYS_SCHEMA = "sys";
72      
73      @Override
74      public Optional<DatabaseAdminExecutor> create(final SQLStatementContext sqlStatementContext) {
75          return Optional.empty();
76      }
77      
78      @Override
79      public Optional<DatabaseAdminExecutor> create(final SQLStatementContext sqlStatementContext, final String sql, final String databaseName, final List<Object> parameters) {
80          if (sqlStatementContext instanceof SelectStatementContext) {
81              return createExecutorForSelectStatement((SelectStatementContext) sqlStatementContext, sql, databaseName, parameters);
82          }
83          SQLStatement sqlStatement = sqlStatementContext.getSqlStatement();
84          if (sqlStatement instanceof MySQLUseStatement) {
85              return Optional.of(new UseDatabaseExecutor((MySQLUseStatement) sqlStatement));
86          }
87          if (sqlStatement instanceof MySQLShowDatabasesStatement) {
88              return Optional.of(new ShowDatabasesExecutor((MySQLShowDatabasesStatement) sqlStatement));
89          }
90          if (sqlStatement instanceof MySQLShowTablesStatement) {
91              return Optional.of(new ShowTablesExecutor((MySQLShowTablesStatement) sqlStatement, sqlStatementContext.getSqlStatement().getDatabaseType()));
92          }
93          if (sqlStatement instanceof MySQLShowCreateDatabaseStatement) {
94              return Optional.of(new ShowCreateDatabaseExecutor((MySQLShowCreateDatabaseStatement) sqlStatement));
95          }
96          if (sqlStatement instanceof MySQLShowFunctionStatusStatement) {
97              return Optional.of(new ShowFunctionStatusExecutor((MySQLShowFunctionStatusStatement) sqlStatement));
98          }
99          if (sqlStatement instanceof MySQLShowProcedureStatusStatement) {
100             return Optional.of(new ShowProcedureStatusExecutor((MySQLShowProcedureStatusStatement) sqlStatement));
101         }
102         if (sqlStatement instanceof SetStatement) {
103             return Optional.of(new MySQLSetVariableAdminExecutor((SetStatement) sqlStatement));
104         }
105         if (sqlStatement instanceof MySQLShowProcessListStatement) {
106             return Optional.of(new ShowProcessListExecutor(((MySQLShowProcessListStatement) sqlStatement).isFull()));
107         }
108         if (sqlStatement instanceof MySQLKillStatement) {
109             return Optional.of(new KillProcessExecutor((MySQLKillStatement) sqlStatement));
110         }
111         return Optional.empty();
112     }
113     
114     private Optional<DatabaseAdminExecutor> createExecutorForSelectStatement(final SelectStatementContext selectStatementContext, final String sql,
115                                                                              final String databaseName, final List<Object> parameters) {
116         if (!selectStatementContext.getSqlStatement().getFrom().isPresent()) {
117             return findAdminExecutorForSelectWithoutFrom(selectStatementContext.getSqlStatement(), sql, databaseName);
118         }
119         if (INFORMATION_SCHEMA.equalsIgnoreCase(databaseName) && !ProxyContext.getInstance().getContextManager().getDatabase(databaseName).isComplete()) {
120             return MySQLInformationSchemaExecutorFactory.newInstance(selectStatementContext, sql, parameters);
121         }
122         if (PERFORMANCE_SCHEMA.equalsIgnoreCase(databaseName) && !ProxyContext.getInstance().getContextManager().getDatabase(databaseName).isComplete()) {
123             return MySQLPerformanceSchemaExecutorFactory.newInstance(selectStatementContext, sql, parameters);
124         }
125         if (MYSQL_SCHEMA.equalsIgnoreCase(databaseName) && !ProxyContext.getInstance().getContextManager().getDatabase(databaseName).isComplete()) {
126             return MySQLMySQLSchemaExecutorFactory.newInstance(selectStatementContext, sql, parameters);
127         }
128         if (SYS_SCHEMA.equalsIgnoreCase(databaseName) && !ProxyContext.getInstance().getContextManager().getDatabase(databaseName).isComplete()) {
129             return MySQLSysSchemaExecutorFactory.newInstance(selectStatementContext, sql, parameters);
130         }
131         return Optional.empty();
132     }
133     
134     private Optional<DatabaseAdminExecutor> findAdminExecutorForSelectWithoutFrom(final SelectStatement selectStatement, final String sql, final String databaseName) {
135         Optional<DatabaseAdminExecutor> result = MySQLSystemVariableQueryExecutor.tryGetSystemVariableQueryExecutor(selectStatement);
136         return result.isPresent() ? result : getSelectFunctionOrVariableExecutor(selectStatement, sql, databaseName);
137     }
138     
139     private Optional<DatabaseAdminExecutor> getSelectFunctionOrVariableExecutor(final SelectStatement selectStatement, final String sql, final String databaseName) {
140         if (isShowSpecialFunction(selectStatement, ShowConnectionIdExecutor.FUNCTION_NAME)) {
141             return Optional.of(new ShowConnectionIdExecutor(selectStatement));
142         }
143         if (isShowSpecialFunction(selectStatement, ShowVersionExecutor.FUNCTION_NAME)) {
144             return Optional.of(new ShowVersionExecutor(selectStatement));
145         }
146         if (isShowSpecialFunction(selectStatement, ShowCurrentUserExecutor.FUNCTION_NAME) || isShowSpecialFunction(selectStatement, ShowCurrentUserExecutor.FUNCTION_NAME_ALIAS)) {
147             return Optional.of(new ShowCurrentUserExecutor());
148         }
149         if (isShowSpecialFunction(selectStatement, ShowCurrentDatabaseExecutor.FUNCTION_NAME)) {
150             return Optional.of(new ShowCurrentDatabaseExecutor());
151         }
152         return mockExecutor(databaseName, selectStatement, sql);
153     }
154     
155     private boolean isShowSpecialFunction(final SelectStatement sqlStatement, final String functionName) {
156         Iterator<ProjectionSegment> segmentIterator = sqlStatement.getProjections().getProjections().iterator();
157         ProjectionSegment firstProjection = segmentIterator.next();
158         return !segmentIterator.hasNext() && firstProjection instanceof ExpressionProjectionSegment && functionName.equalsIgnoreCase(((ExpressionProjectionSegment) firstProjection).getText());
159     }
160     
161     private Optional<DatabaseAdminExecutor> mockExecutor(final String databaseName, final SelectStatement sqlStatement, final String sql) {
162         if (hasNoResource()) {
163             return Optional.of(new NoResourceShowExecutor(sqlStatement));
164         }
165         boolean isUseSchema = null != databaseName || sqlStatement.getFrom().isPresent();
166         return isUseSchema ? Optional.empty() : Optional.of(new UnicastResourceShowExecutor(sqlStatement, sql));
167     }
168     
169     private boolean hasNoResource() {
170         Collection<String> databaseNames = ProxyContext.getInstance().getAllDatabaseNames();
171         if (databaseNames.isEmpty()) {
172             return true;
173         }
174         for (String each : databaseNames) {
175             if (ProxyContext.getInstance().getContextManager().getDatabase(each).containsDataSource()) {
176                 return false;
177             }
178         }
179         return true;
180     }
181     
182     @Override
183     public String getDatabaseType() {
184         return "MySQL";
185     }
186 }