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.proxy.backend.context.ProxyContext;
22  import org.apache.shardingsphere.proxy.backend.handler.admin.executor.DatabaseAdminExecutor;
23  import org.apache.shardingsphere.proxy.backend.handler.admin.executor.DatabaseAdminExecutorCreator;
24  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.KillProcessExecutor;
25  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.MySQLSetVariableAdminExecutor;
26  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.MySQLSystemVariableQueryExecutor;
27  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.NoResourceShowExecutor;
28  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowConnectionIdExecutor;
29  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowCreateDatabaseExecutor;
30  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowCurrentDatabaseExecutor;
31  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowCurrentUserExecutor;
32  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowDatabasesExecutor;
33  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowFunctionStatusExecutor;
34  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowProcedureStatusExecutor;
35  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowProcessListExecutor;
36  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowTablesExecutor;
37  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.ShowVersionExecutor;
38  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.UnicastResourceShowExecutor;
39  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.UseDatabaseExecutor;
40  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ExpressionProjectionSegment;
41  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ProjectionSegment;
42  import org.apache.shardingsphere.sql.parser.sql.common.statement.SQLStatement;
43  import org.apache.shardingsphere.sql.parser.sql.common.statement.dal.SetStatement;
44  import org.apache.shardingsphere.sql.parser.sql.common.statement.dal.UseStatement;
45  import org.apache.shardingsphere.sql.parser.sql.common.statement.dml.SelectStatement;
46  import org.apache.shardingsphere.sql.parser.sql.dialect.statement.mysql.dal.MySQLKillStatement;
47  import org.apache.shardingsphere.sql.parser.sql.dialect.statement.mysql.dal.MySQLShowCreateDatabaseStatement;
48  import org.apache.shardingsphere.sql.parser.sql.dialect.statement.mysql.dal.MySQLShowDatabasesStatement;
49  import org.apache.shardingsphere.sql.parser.sql.dialect.statement.mysql.dal.MySQLShowFunctionStatusStatement;
50  import org.apache.shardingsphere.sql.parser.sql.dialect.statement.mysql.dal.MySQLShowProcedureStatusStatement;
51  import org.apache.shardingsphere.sql.parser.sql.dialect.statement.mysql.dal.MySQLShowProcessListStatement;
52  import org.apache.shardingsphere.sql.parser.sql.dialect.statement.mysql.dal.MySQLShowTablesStatement;
53  
54  import java.util.Collection;
55  import java.util.Iterator;
56  import java.util.List;
57  import java.util.Optional;
58  
59  /**
60   * Database admin executor creator for MySQL.
61   */
62  public final class MySQLAdminExecutorCreator implements DatabaseAdminExecutorCreator {
63      
64      private static final String INFORMATION_SCHEMA = "information_schema";
65      
66      private static final String MYSQL_SCHEMA = "mysql";
67      
68      private static final String PERFORMANCE_SCHEMA = "performance_schema";
69      
70      private static final String SYS_SCHEMA = "sys";
71      
72      @Override
73      public Optional<DatabaseAdminExecutor> create(final SQLStatementContext sqlStatementContext) {
74          SQLStatement sqlStatement = sqlStatementContext.getSqlStatement();
75          if (sqlStatement instanceof MySQLShowFunctionStatusStatement) {
76              return Optional.of(new ShowFunctionStatusExecutor((MySQLShowFunctionStatusStatement) sqlStatement));
77          }
78          if (sqlStatement instanceof MySQLShowProcedureStatusStatement) {
79              return Optional.of(new ShowProcedureStatusExecutor((MySQLShowProcedureStatusStatement) sqlStatement));
80          }
81          if (sqlStatement instanceof MySQLShowTablesStatement) {
82              return Optional.of(new ShowTablesExecutor((MySQLShowTablesStatement) sqlStatement, sqlStatementContext.getDatabaseType()));
83          }
84          return Optional.empty();
85      }
86      
87      @Override
88      public Optional<DatabaseAdminExecutor> create(final SQLStatementContext sqlStatementContext, final String sql, final String databaseName, final List<Object> parameters) {
89          SQLStatement sqlStatement = sqlStatementContext.getSqlStatement();
90          if (sqlStatement instanceof UseStatement) {
91              return Optional.of(new UseDatabaseExecutor((UseStatement) sqlStatement));
92          }
93          if (sqlStatement instanceof MySQLShowDatabasesStatement) {
94              return Optional.of(new ShowDatabasesExecutor((MySQLShowDatabasesStatement) sqlStatement));
95          }
96          if (sqlStatement instanceof MySQLShowProcessListStatement) {
97              return Optional.of(new ShowProcessListExecutor(((MySQLShowProcessListStatement) sqlStatement).isFull()));
98          }
99          if (sqlStatement instanceof MySQLKillStatement) {
100             return Optional.of(new KillProcessExecutor((MySQLKillStatement) sqlStatement));
101         }
102         if (sqlStatement instanceof MySQLShowCreateDatabaseStatement) {
103             return Optional.of(new ShowCreateDatabaseExecutor((MySQLShowCreateDatabaseStatement) sqlStatement));
104         }
105         if (sqlStatement instanceof SetStatement) {
106             return Optional.of(new MySQLSetVariableAdminExecutor((SetStatement) sqlStatement));
107         }
108         if (sqlStatement instanceof SelectStatement) {
109             return create((SelectStatement) sqlStatement, sql, databaseName, parameters);
110         }
111         return Optional.empty();
112     }
113     
114     private Optional<DatabaseAdminExecutor> create(final SelectStatement selectStatement, final String sql, final String databaseName, final List<Object> parameters) {
115         if (!selectStatement.getFrom().isPresent()) {
116             return findAdminExecutorForSelectWithoutFrom(sql, databaseName, selectStatement);
117         }
118         if (isQueryInformationSchema(databaseName)) {
119             return MySQLInformationSchemaExecutorFactory.newInstance(selectStatement, sql, parameters);
120         }
121         if (isQueryPerformanceSchema(databaseName)) {
122             return MySQLPerformanceSchemaExecutorFactory.newInstance(selectStatement, sql, parameters);
123         }
124         if (isQueryMySQLSchema(databaseName)) {
125             return MySQLMySQLSchemaExecutorFactory.newInstance(selectStatement, sql, parameters);
126         }
127         if (isQuerySysSchema(databaseName)) {
128             return MySQLSysSchemaExecutorFactory.newInstance(selectStatement, sql, parameters);
129         }
130         return Optional.empty();
131     }
132     
133     private Optional<DatabaseAdminExecutor> findAdminExecutorForSelectWithoutFrom(final String sql, final String databaseName, final SelectStatement selectStatement) {
134         Optional<DatabaseAdminExecutor> result = MySQLSystemVariableQueryExecutor.tryGetSystemVariableQueryExecutor(selectStatement);
135         return result.isPresent() ? result : getSelectFunctionOrVariableExecutor(selectStatement, sql, databaseName);
136     }
137     
138     private Optional<DatabaseAdminExecutor> getSelectFunctionOrVariableExecutor(final SelectStatement selectStatement, final String sql, final String databaseName) {
139         if (isShowSpecialFunction(selectStatement, ShowConnectionIdExecutor.FUNCTION_NAME)) {
140             return Optional.of(new ShowConnectionIdExecutor(selectStatement));
141         }
142         if (isShowSpecialFunction(selectStatement, ShowVersionExecutor.FUNCTION_NAME)) {
143             return Optional.of(new ShowVersionExecutor(selectStatement));
144         }
145         if (isShowSpecialFunction(selectStatement, ShowCurrentUserExecutor.FUNCTION_NAME)
146                 || 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
159                 && functionName.equalsIgnoreCase(((ExpressionProjectionSegment) firstProjection).getText());
160     }
161     
162     private boolean isQueryInformationSchema(final String databaseName) {
163         // TODO remove DefaultDatabaseMetaDataExecutor when sql federation can support all system table query
164         return INFORMATION_SCHEMA.equalsIgnoreCase(databaseName) && !ProxyContext.getInstance().getContextManager().getDatabase(databaseName).isComplete();
165     }
166     
167     private boolean isQueryPerformanceSchema(final String databaseName) {
168         return PERFORMANCE_SCHEMA.equalsIgnoreCase(databaseName);
169     }
170     
171     private boolean isQueryMySQLSchema(final String databaseName) {
172         return MYSQL_SCHEMA.equalsIgnoreCase(databaseName);
173     }
174     
175     private boolean isQuerySysSchema(final String databaseName) {
176         return SYS_SCHEMA.equalsIgnoreCase(databaseName);
177     }
178     
179     private Optional<DatabaseAdminExecutor> mockExecutor(final String databaseName, final SelectStatement sqlStatement, final String sql) {
180         if (hasNoResource()) {
181             return Optional.of(new NoResourceShowExecutor(sqlStatement));
182         }
183         boolean isNotUseSchema = null == databaseName && !sqlStatement.getFrom().isPresent();
184         return isNotUseSchema ? Optional.of(new UnicastResourceShowExecutor(sqlStatement, sql)) : Optional.empty();
185     }
186     
187     private boolean hasNoResource() {
188         Collection<String> databaseNames = ProxyContext.getInstance().getAllDatabaseNames();
189         if (databaseNames.isEmpty()) {
190             return true;
191         }
192         for (String each : databaseNames) {
193             if (ProxyContext.getInstance().getContextManager().getDatabase(each).containsDataSource()) {
194                 return false;
195             }
196         }
197         return true;
198     }
199     
200     @Override
201     public String getDatabaseType() {
202         return "MySQL";
203     }
204 }