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.infra.merge;
19  
20  import org.apache.shardingsphere.infra.annotation.HighFrequencyInvocation;
21  import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
22  import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
23  import org.apache.shardingsphere.infra.executor.sql.execute.result.query.QueryResult;
24  import org.apache.shardingsphere.infra.merge.engine.ResultProcessEngine;
25  import org.apache.shardingsphere.infra.merge.engine.decorator.ResultDecorator;
26  import org.apache.shardingsphere.infra.merge.engine.decorator.ResultDecoratorEngine;
27  import org.apache.shardingsphere.infra.merge.engine.decorator.impl.TransparentResultDecorator;
28  import org.apache.shardingsphere.infra.merge.engine.merger.ResultMerger;
29  import org.apache.shardingsphere.infra.merge.engine.merger.ResultMergerEngine;
30  import org.apache.shardingsphere.infra.merge.result.MergedResult;
31  import org.apache.shardingsphere.infra.merge.result.impl.transparent.TransparentMergedResult;
32  import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
33  import org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
34  import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
35  import org.apache.shardingsphere.infra.session.connection.ConnectionContext;
36  import org.apache.shardingsphere.infra.session.query.QueryContext;
37  import org.apache.shardingsphere.infra.spi.type.ordered.OrderedSPILoader;
38  
39  import java.sql.SQLException;
40  import java.util.List;
41  import java.util.Map;
42  import java.util.Map.Entry;
43  import java.util.Optional;
44  
45  /**
46   * Merge engine.
47   */
48  @HighFrequencyInvocation
49  public final class MergeEngine {
50      
51      private final ShardingSphereMetaData metaData;
52      
53      private final ShardingSphereDatabase database;
54      
55      private final ConfigurationProperties props;
56      
57      @SuppressWarnings("rawtypes")
58      private final Map<ShardingSphereRule, ResultProcessEngine> engines;
59      
60      private final ConnectionContext connectionContext;
61      
62      public MergeEngine(final ShardingSphereMetaData metaData, final ShardingSphereDatabase database, final ConfigurationProperties props, final ConnectionContext connectionContext) {
63          this.metaData = metaData;
64          this.database = database;
65          this.props = props;
66          engines = OrderedSPILoader.getServices(ResultProcessEngine.class, database.getRuleMetaData().getRules());
67          this.connectionContext = connectionContext;
68      }
69      
70      /**
71       * Merge.
72       *
73       * @param queryResults query results
74       * @param queryContext SQL statement context
75       * @return merged result
76       * @throws SQLException SQL exception
77       */
78      public MergedResult merge(final List<QueryResult> queryResults, final QueryContext queryContext) throws SQLException {
79          MergedResult mergedResult = executeMerge(queryResults, queryContext).orElseGet(() -> new TransparentMergedResult(queryResults.get(0)));
80          return decorate(mergedResult, queryContext);
81      }
82      
83      @SuppressWarnings({"unchecked", "rawtypes"})
84      private Optional<MergedResult> executeMerge(final List<QueryResult> queryResults, final QueryContext queryContext) throws SQLException {
85          for (Entry<ShardingSphereRule, ResultProcessEngine> entry : engines.entrySet()) {
86              if (entry.getValue() instanceof ResultMergerEngine) {
87                  ResultMerger resultMerger =
88                          ((ResultMergerEngine) entry.getValue()).newInstance(database.getName(), database.getProtocolType(), entry.getKey(), props, queryContext.getSqlStatementContext());
89                  return Optional.of(resultMerger.merge(queryResults, queryContext.getSqlStatementContext(), database, connectionContext));
90              }
91          }
92          return Optional.empty();
93      }
94      
95      @SuppressWarnings({"unchecked", "rawtypes"})
96      private MergedResult decorate(final MergedResult mergedResult, final QueryContext queryContext) throws SQLException {
97          MergedResult result = null;
98          for (Entry<ShardingSphereRule, ResultProcessEngine> entry : engines.entrySet()) {
99              if (entry.getValue() instanceof ResultDecoratorEngine) {
100                 ResultDecorator resultDecorator = getResultDecorator(queryContext.getSqlStatementContext(), entry.getValue());
101                 result = null == result ? resultDecorator.decorate(mergedResult, queryContext, entry.getKey())
102                         : resultDecorator.decorate(result, queryContext, entry.getKey());
103             }
104         }
105         return null == result ? mergedResult : result;
106     }
107     
108     @SuppressWarnings({"unchecked", "rawtypes"})
109     private ResultDecorator getResultDecorator(final SQLStatementContext sqlStatementContext, final ResultProcessEngine resultProcessEngine) {
110         return (ResultDecorator) ((ResultDecoratorEngine) resultProcessEngine).newInstance(metaData, database, props, sqlStatementContext).orElseGet(TransparentResultDecorator::new);
111     }
112 }