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.sqlfederation.optimizer.planner.util;
19  
20  import lombok.AccessLevel;
21  import lombok.NoArgsConstructor;
22  import org.apache.calcite.adapter.enumerable.EnumerableRules;
23  import org.apache.calcite.config.CalciteConnectionConfig;
24  import org.apache.calcite.jdbc.CalciteSchema;
25  import org.apache.calcite.plan.ConventionTraitDef;
26  import org.apache.calcite.plan.RelOptCluster;
27  import org.apache.calcite.plan.RelOptPlanner;
28  import org.apache.calcite.plan.RelOptRule;
29  import org.apache.calcite.plan.RelOptTable.ViewExpander;
30  import org.apache.calcite.plan.hep.HepMatchOrder;
31  import org.apache.calcite.plan.hep.HepPlanner;
32  import org.apache.calcite.plan.hep.HepProgramBuilder;
33  import org.apache.calcite.plan.volcano.VolcanoPlanner;
34  import org.apache.calcite.prepare.CalciteCatalogReader;
35  import org.apache.calcite.rel.RelCollationTraitDef;
36  import org.apache.calcite.rel.rules.AggregateExpandDistinctAggregatesRule;
37  import org.apache.calcite.rel.rules.CoreRules;
38  import org.apache.calcite.rel.rules.ProjectRemoveRule;
39  import org.apache.calcite.rel.type.RelDataTypeFactory;
40  import org.apache.calcite.rex.RexBuilder;
41  import org.apache.calcite.schema.Schema;
42  import org.apache.calcite.sql.SqlOperatorTable;
43  import org.apache.calcite.sql.fun.SqlLibrary;
44  import org.apache.calcite.sql.fun.SqlLibraryOperatorTableFactory;
45  import org.apache.calcite.sql.util.SqlOperatorTables;
46  import org.apache.calcite.sql.validate.SqlValidator;
47  import org.apache.calcite.sql.validate.SqlValidatorUtil;
48  import org.apache.calcite.sql2rel.SqlToRelConverter;
49  import org.apache.calcite.sql2rel.SqlToRelConverter.Config;
50  import org.apache.calcite.sql2rel.StandardConvertletTable;
51  import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
52  import org.apache.shardingsphere.parser.rule.SQLParserRule;
53  import org.apache.shardingsphere.sqlfederation.optimizer.metadata.view.ShardingSphereViewExpander;
54  import org.apache.shardingsphere.sqlfederation.optimizer.planner.rule.converter.EnumerableModifyConverterRule;
55  import org.apache.shardingsphere.sqlfederation.optimizer.planner.rule.converter.EnumerableScanConverterRule;
56  import org.apache.shardingsphere.sqlfederation.optimizer.planner.rule.transformation.PushFilterIntoScanRule;
57  import org.apache.shardingsphere.sqlfederation.optimizer.planner.rule.transformation.PushProjectIntoScanRule;
58  
59  import java.util.Arrays;
60  import java.util.Collection;
61  import java.util.Collections;
62  import java.util.HashMap;
63  import java.util.LinkedList;
64  import java.util.Map;
65  
66  /**
67   * SQL federation planner utility class.
68   */
69  @NoArgsConstructor(access = AccessLevel.PRIVATE)
70  public final class SQLFederationPlannerUtils {
71      
72      private static final int DEFAULT_MATCH_LIMIT = 1024;
73      
74      private static final Map<String, SqlLibrary> DATABASE_TYPE_SQL_LIBRARIES = new HashMap<>();
75      
76      static {
77          DATABASE_TYPE_SQL_LIBRARIES.put("MySQL", SqlLibrary.MYSQL);
78          DATABASE_TYPE_SQL_LIBRARIES.put("PostgreSQL", SqlLibrary.POSTGRESQL);
79          DATABASE_TYPE_SQL_LIBRARIES.put("openGauss", SqlLibrary.POSTGRESQL);
80          DATABASE_TYPE_SQL_LIBRARIES.put("Oracle", SqlLibrary.ORACLE);
81      }
82      
83      /**
84       * Create new instance of volcano planner.
85       *
86       * @return volcano planner instance
87       */
88      public static RelOptPlanner createVolcanoPlanner() {
89          RelOptPlanner result = new VolcanoPlanner();
90          setUpRules(result);
91          return result;
92      }
93      
94      /**
95       * Create new instance of hep planner.
96       *
97       * @return hep planner instance
98       */
99      public static RelOptPlanner createHepPlanner() {
100         HepProgramBuilder builder = new HepProgramBuilder();
101         builder.addGroupBegin().addRuleCollection(getFilterRules()).addGroupEnd().addMatchOrder(HepMatchOrder.BOTTOM_UP);
102         builder.addGroupBegin().addRuleCollection(getProjectRules()).addGroupEnd().addMatchOrder(HepMatchOrder.BOTTOM_UP);
103         builder.addGroupBegin().addRuleCollection(getAggregationRules()).addGroupEnd().addMatchOrder(HepMatchOrder.BOTTOM_UP);
104         builder.addGroupBegin().addRuleCollection(getCalcRules()).addGroupEnd().addMatchOrder(HepMatchOrder.BOTTOM_UP);
105         builder.addGroupBegin().addRuleCollection(getSubQueryRules()).addGroupEnd().addMatchOrder(HepMatchOrder.BOTTOM_UP);
106         builder.addMatchLimit(DEFAULT_MATCH_LIMIT);
107         return new HepPlanner(builder.build());
108     }
109     
110     private static void setUpRules(final RelOptPlanner planner) {
111         planner.addRelTraitDef(ConventionTraitDef.INSTANCE);
112         planner.addRelTraitDef(RelCollationTraitDef.INSTANCE);
113         planner.addRule(EnumerableRules.ENUMERABLE_JOIN_RULE);
114         planner.addRule(EnumerableRules.ENUMERABLE_MERGE_JOIN_RULE);
115         planner.addRule(EnumerableRules.ENUMERABLE_CORRELATE_RULE);
116         planner.addRule(EnumerableRules.ENUMERABLE_PROJECT_RULE);
117         planner.addRule(EnumerableRules.ENUMERABLE_FILTER_RULE);
118         planner.addRule(EnumerableRules.ENUMERABLE_CALC_RULE);
119         planner.addRule(EnumerableRules.ENUMERABLE_AGGREGATE_RULE);
120         planner.addRule(EnumerableRules.ENUMERABLE_SORT_RULE);
121         planner.addRule(EnumerableRules.ENUMERABLE_LIMIT_RULE);
122         planner.addRule(EnumerableRules.ENUMERABLE_COLLECT_RULE);
123         planner.addRule(EnumerableRules.ENUMERABLE_UNCOLLECT_RULE);
124         planner.addRule(EnumerableRules.ENUMERABLE_UNION_RULE);
125         planner.addRule(EnumerableRules.ENUMERABLE_REPEAT_UNION_RULE);
126         planner.addRule(EnumerableRules.ENUMERABLE_TABLE_SPOOL_RULE);
127         planner.addRule(EnumerableRules.ENUMERABLE_INTERSECT_RULE);
128         planner.addRule(EnumerableRules.ENUMERABLE_MINUS_RULE);
129         planner.addRule(EnumerableRules.ENUMERABLE_VALUES_RULE);
130         planner.addRule(EnumerableRules.ENUMERABLE_WINDOW_RULE);
131         planner.addRule(EnumerableRules.ENUMERABLE_TABLE_SCAN_RULE);
132         planner.addRule(EnumerableRules.ENUMERABLE_TABLE_FUNCTION_SCAN_RULE);
133         planner.addRule(EnumerableRules.ENUMERABLE_MATCH_RULE);
134         planner.addRule(EnumerableScanConverterRule.DEFAULT_CONFIG.toRule());
135         planner.addRule(EnumerableModifyConverterRule.DEFAULT_CONFIG.toRule());
136     }
137     
138     private static Collection<RelOptRule> getSubQueryRules() {
139         Collection<RelOptRule> result = new LinkedList<>();
140         result.add(CoreRules.FILTER_SUB_QUERY_TO_CORRELATE);
141         result.add(CoreRules.PROJECT_SUB_QUERY_TO_CORRELATE);
142         result.add(CoreRules.JOIN_SUB_QUERY_TO_CORRELATE);
143         return result;
144     }
145     
146     private static Collection<RelOptRule> getCalcRules() {
147         Collection<RelOptRule> result = new LinkedList<>();
148         result.add(AggregateExpandDistinctAggregatesRule.Config.DEFAULT.toRule());
149         result.add(CoreRules.PROJECT_TO_CALC);
150         result.add(CoreRules.FILTER_TO_CALC);
151         result.add(CoreRules.PROJECT_CALC_MERGE);
152         result.add(CoreRules.FILTER_CALC_MERGE);
153         result.add(EnumerableRules.ENUMERABLE_FILTER_TO_CALC_RULE);
154         result.add(EnumerableRules.ENUMERABLE_PROJECT_TO_CALC_RULE);
155         result.add(CoreRules.CALC_MERGE);
156         return result;
157     }
158     
159     private static Collection<RelOptRule> getProjectRules() {
160         Collection<RelOptRule> result = new LinkedList<>();
161         result.add(CoreRules.PROJECT_MERGE);
162         result.add(CoreRules.PROJECT_CORRELATE_TRANSPOSE);
163         result.add(CoreRules.PROJECT_SET_OP_TRANSPOSE);
164         result.add(CoreRules.PROJECT_JOIN_TRANSPOSE);
165         result.add(CoreRules.PROJECT_REDUCE_EXPRESSIONS);
166         result.add(ProjectRemoveRule.Config.DEFAULT.toRule());
167         result.add(PushProjectIntoScanRule.Config.DEFAULT.toRule());
168         return result;
169     }
170     
171     private static Collection<RelOptRule> getFilterRules() {
172         Collection<RelOptRule> result = new LinkedList<>();
173         result.add(CoreRules.FILTER_INTO_JOIN);
174         result.add(CoreRules.JOIN_CONDITION_PUSH);
175         result.add(CoreRules.SORT_JOIN_TRANSPOSE);
176         result.add(CoreRules.FILTER_AGGREGATE_TRANSPOSE);
177         result.add(CoreRules.FILTER_PROJECT_TRANSPOSE);
178         result.add(CoreRules.FILTER_SET_OP_TRANSPOSE);
179         result.add(CoreRules.FILTER_REDUCE_EXPRESSIONS);
180         result.add(CoreRules.FILTER_MERGE);
181         result.add(CoreRules.JOIN_PUSH_EXPRESSIONS);
182         result.add(CoreRules.JOIN_PUSH_TRANSITIVE_PREDICATES);
183         result.add(PushFilterIntoScanRule.Config.DEFAULT.toRule());
184         return result;
185     }
186     
187     private static Collection<RelOptRule> getAggregationRules() {
188         Collection<RelOptRule> result = new LinkedList<>();
189         result.add(CoreRules.AGGREGATE_MERGE);
190         result.add(CoreRules.AGGREGATE_REDUCE_FUNCTIONS);
191         return result;
192     }
193     
194     /**
195      * Create catalog reader.
196      *
197      * @param schemaName schema name
198      * @param schema schema
199      * @param relDataTypeFactory rel data type factory
200      * @param connectionConfig connection config
201      * @return calcite catalog reader
202      */
203     public static CalciteCatalogReader createCatalogReader(final String schemaName, final Schema schema, final RelDataTypeFactory relDataTypeFactory, final CalciteConnectionConfig connectionConfig) {
204         CalciteSchema rootSchema = CalciteSchema.createRootSchema(true);
205         rootSchema.add(schemaName, schema);
206         SQLFederationFunctionUtils.registryUserDefinedFunction(schemaName, rootSchema.plus());
207         return new CalciteCatalogReader(rootSchema, Collections.singletonList(schemaName), relDataTypeFactory, connectionConfig);
208     }
209     
210     /**
211      * Create sql validator.
212      *
213      * @param catalogReader catalog reader
214      * @param relDataTypeFactory rel data type factory
215      * @param databaseType database type
216      * @param connectionConfig connection config
217      * @return sql validator
218      */
219     public static SqlValidator createSqlValidator(final CalciteCatalogReader catalogReader, final RelDataTypeFactory relDataTypeFactory,
220                                                   final DatabaseType databaseType, final CalciteConnectionConfig connectionConfig) {
221         SqlValidator.Config validatorConfig = SqlValidator.Config.DEFAULT
222                 .withLenientOperatorLookup(connectionConfig.lenientOperatorLookup())
223                 .withConformance(connectionConfig.conformance())
224                 .withDefaultNullCollation(connectionConfig.defaultNullCollation())
225                 .withIdentifierExpansion(true);
226         SqlOperatorTable sqlOperatorTable = getSQLOperatorTable(catalogReader, databaseType.getTrunkDatabaseType().orElse(databaseType));
227         return SqlValidatorUtil.newValidator(sqlOperatorTable, catalogReader, relDataTypeFactory, validatorConfig);
228     }
229     
230     private static SqlOperatorTable getSQLOperatorTable(final CalciteCatalogReader catalogReader, final DatabaseType databaseType) {
231         return SqlOperatorTables.chain(Arrays.asList(SqlLibraryOperatorTableFactory.INSTANCE.getOperatorTable(
232                 Arrays.asList(SqlLibrary.STANDARD, DATABASE_TYPE_SQL_LIBRARIES.getOrDefault(databaseType.getType(), SqlLibrary.MYSQL))), catalogReader));
233     }
234     
235     /**
236      * Create sql to rel converter.
237      * 
238      * @param catalogReader catalog reader
239      * @param validator validator
240      * @param cluster cluster
241      * @param sqlParserRule sql parser rule
242      * @param databaseType database type
243      * @param needsViewExpand whether sql needs view expand or not
244      * @return sql to rel converter
245      */
246     public static SqlToRelConverter createSqlToRelConverter(final CalciteCatalogReader catalogReader, final SqlValidator validator, final RelOptCluster cluster,
247                                                             final SQLParserRule sqlParserRule, final DatabaseType databaseType, final boolean needsViewExpand) {
248         ViewExpander expander = needsViewExpand ? new ShardingSphereViewExpander(sqlParserRule, databaseType,
249                 createSqlToRelConverter(catalogReader, validator, cluster, sqlParserRule, databaseType, false)) : (rowType, queryString, schemaPath, viewPath) -> null;
250         // TODO remove withRemoveSortInSubQuery when calcite can expand view which contains order by correctly
251         Config converterConfig = SqlToRelConverter.config().withTrimUnusedFields(true).withRemoveSortInSubQuery(false).withExpand(true);
252         return new SqlToRelConverter(expander, validator, catalogReader, cluster, StandardConvertletTable.INSTANCE, converterConfig);
253     }
254     
255     /**
256      * Create rel opt cluster.
257      * 
258      * @param relDataTypeFactory rel data type factory
259      * @return rel opt cluster
260      */
261     public static RelOptCluster createRelOptCluster(final RelDataTypeFactory relDataTypeFactory) {
262         return RelOptCluster.create(SQLFederationPlannerUtils.createVolcanoPlanner(), new RexBuilder(relDataTypeFactory));
263     }
264 }