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