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.executor;
19  
20  import lombok.RequiredArgsConstructor;
21  import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
22  import org.apache.shardingsphere.infra.binder.engine.SQLBindEngine;
23  import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
24  import org.apache.shardingsphere.infra.exception.mysql.exception.UnknownSystemVariableException;
25  import org.apache.shardingsphere.infra.hint.HintValueContext;
26  import org.apache.shardingsphere.infra.session.query.QueryContext;
27  import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
28  import org.apache.shardingsphere.mode.metadata.MetaDataContexts;
29  import org.apache.shardingsphere.parser.rule.SQLParserRule;
30  import org.apache.shardingsphere.proxy.backend.connector.DatabaseConnectorFactory;
31  import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
32  import org.apache.shardingsphere.proxy.backend.handler.admin.executor.DatabaseAdminExecutor;
33  import org.apache.shardingsphere.proxy.backend.handler.admin.executor.variable.charset.CharsetSetExecutor;
34  import org.apache.shardingsphere.proxy.backend.handler.admin.executor.variable.session.SessionVariableRecordExecutor;
35  import org.apache.shardingsphere.proxy.backend.handler.data.DatabaseBackendHandler;
36  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.sysvar.MySQLSystemVariable;
37  import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.sysvar.Scope;
38  import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
39  import org.apache.shardingsphere.sql.parser.sql.common.segment.dal.VariableAssignSegment;
40  import org.apache.shardingsphere.sql.parser.sql.common.statement.SQLStatement;
41  import org.apache.shardingsphere.sql.parser.sql.common.statement.dal.SetStatement;
42  
43  import java.sql.SQLException;
44  import java.util.Collection;
45  import java.util.Collections;
46  import java.util.LinkedHashMap;
47  import java.util.Map;
48  import java.util.stream.Collectors;
49  
50  /**
51   * Set variable admin executor for MySQL.
52   */
53  @RequiredArgsConstructor
54  public final class MySQLSetVariableAdminExecutor implements DatabaseAdminExecutor {
55      
56      private final DatabaseType databaseType = TypedSPILoader.getService(DatabaseType.class, "MySQL");
57      
58      private final SetStatement setStatement;
59      
60      @Override
61      public void execute(final ConnectionSession connectionSession) throws SQLException {
62          Map<String, String> sessionVariables = extractSessionVariables();
63          validateSessionVariables(sessionVariables.keySet());
64          new CharsetSetExecutor(databaseType, connectionSession).set(sessionVariables);
65          new SessionVariableRecordExecutor(databaseType, connectionSession).recordVariable(sessionVariables);
66          executeSetGlobalVariablesIfPresent(connectionSession);
67      }
68      
69      private Map<String, String> extractSessionVariables() {
70          return setStatement.getVariableAssigns().stream().filter(each -> !"global".equalsIgnoreCase(each.getVariable().getScope().orElse("")))
71                  .collect(Collectors.toMap(each -> each.getVariable().getVariable(), VariableAssignSegment::getAssignValue));
72      }
73      
74      private void validateSessionVariables(final Collection<String> sessionVariables) {
75          for (String each : sessionVariables) {
76              MySQLSystemVariable systemVariable = MySQLSystemVariable.findSystemVariable(each).orElseThrow(() -> new UnknownSystemVariableException(each));
77              systemVariable.validateSetTargetScope(Scope.SESSION);
78          }
79      }
80      
81      private void executeSetGlobalVariablesIfPresent(final ConnectionSession connectionSession) throws SQLException {
82          if (null == connectionSession.getDatabaseName()) {
83              return;
84          }
85          String concatenatedGlobalVariables = extractGlobalVariables().entrySet().stream().map(entry -> String.format("@@GLOBAL.%s = %s", entry.getKey(), entry.getValue()))
86                  .collect(Collectors.joining(", "));
87          if (concatenatedGlobalVariables.isEmpty()) {
88              return;
89          }
90          String sql = "SET " + concatenatedGlobalVariables;
91          MetaDataContexts metaDataContexts = ProxyContext.getInstance().getContextManager().getMetaDataContexts();
92          SQLParserRule sqlParserRule = metaDataContexts.getMetaData().getGlobalRuleMetaData().getSingleRule(SQLParserRule.class);
93          SQLStatement sqlStatement = sqlParserRule.getSQLParserEngine(TypedSPILoader.getService(DatabaseType.class, "MySQL")).parse(sql, false);
94          SQLStatementContext sqlStatementContext = new SQLBindEngine(ProxyContext.getInstance().getContextManager().getMetaDataContexts().getMetaData(),
95                  connectionSession.getDefaultDatabaseName(), new HintValueContext()).bind(sqlStatement, Collections.emptyList());
96          DatabaseBackendHandler databaseBackendHandler = DatabaseConnectorFactory.getInstance()
97                  .newInstance(new QueryContext(sqlStatementContext, sql, Collections.emptyList(), new HintValueContext()), connectionSession.getDatabaseConnectionManager(), false);
98          try {
99              databaseBackendHandler.execute();
100         } finally {
101             databaseBackendHandler.close();
102         }
103     }
104     
105     private Map<String, String> extractGlobalVariables() {
106         return setStatement.getVariableAssigns().stream().filter(each -> "global".equalsIgnoreCase(each.getVariable().getScope().orElse("")))
107                 .collect(Collectors.toMap(each -> each.getVariable().getVariable(), VariableAssignSegment::getAssignValue, (oldValue, newValue) -> newValue, LinkedHashMap::new));
108     }
109 }