1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.shardingsphere.data.pipeline.core.metadata.generator;
19
20 import com.google.common.base.Strings;
21 import lombok.extern.slf4j.Slf4j;
22 import org.apache.shardingsphere.data.pipeline.core.sqlbuilder.dialect.DialectPipelineSQLBuilder;
23 import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
24 import org.apache.shardingsphere.infra.binder.context.statement.ddl.AlterTableStatementContext;
25 import org.apache.shardingsphere.infra.binder.context.statement.ddl.CommentStatementContext;
26 import org.apache.shardingsphere.infra.binder.context.statement.ddl.CreateIndexStatementContext;
27 import org.apache.shardingsphere.infra.binder.context.statement.ddl.CreateTableStatementContext;
28 import org.apache.shardingsphere.infra.binder.context.type.ConstraintAvailable;
29 import org.apache.shardingsphere.infra.binder.context.type.IndexAvailable;
30 import org.apache.shardingsphere.infra.binder.context.type.TableAvailable;
31 import org.apache.shardingsphere.infra.binder.engine.SQLBindEngine;
32 import org.apache.shardingsphere.infra.database.core.spi.DatabaseTypedSPILoader;
33 import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
34 import org.apache.shardingsphere.infra.hint.HintValueContext;
35 import org.apache.shardingsphere.infra.metadata.database.schema.util.IndexMetaDataUtils;
36 import org.apache.shardingsphere.infra.parser.SQLParserEngine;
37 import org.apache.shardingsphere.infra.session.query.QueryContext;
38 import org.apache.shardingsphere.sql.parser.sql.common.segment.SQLSegment;
39 import org.apache.shardingsphere.sql.parser.sql.common.segment.ddl.constraint.ConstraintSegment;
40 import org.apache.shardingsphere.sql.parser.sql.common.segment.ddl.index.IndexSegment;
41 import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.SimpleTableSegment;
42 import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.TableNameSegment;
43
44 import javax.sql.DataSource;
45 import java.sql.Connection;
46 import java.sql.SQLException;
47 import java.util.Collections;
48 import java.util.Comparator;
49 import java.util.Map;
50 import java.util.Map.Entry;
51 import java.util.Optional;
52 import java.util.TreeMap;
53
54
55
56
57 @Slf4j
58 public final class PipelineDDLGenerator {
59
60 private static final String DELIMITER = ";";
61
62 private static final String SET_SEARCH_PATH_PREFIX = "set search_path";
63
64
65
66
67
68
69
70
71
72
73
74
75
76 public String generateLogicDDL(final DatabaseType databaseType, final DataSource sourceDataSource,
77 final String schemaName, final String sourceTableName, final String targetTableName, final SQLParserEngine parserEngine) throws SQLException {
78 long startTimeMillis = System.currentTimeMillis();
79 StringBuilder result = new StringBuilder();
80 for (String each : DatabaseTypedSPILoader.getService(DialectPipelineSQLBuilder.class, databaseType).buildCreateTableSQLs(sourceDataSource, schemaName, sourceTableName)) {
81 Optional<String> queryContext = decorate(databaseType, sourceDataSource, schemaName, targetTableName, parserEngine, each);
82 queryContext.ifPresent(optional -> result.append(optional).append(DELIMITER).append(System.lineSeparator()));
83 }
84 log.info("generateLogicDDL, databaseType={}, schemaName={}, sourceTableName={}, targetTableName={}, cost {} ms",
85 databaseType.getType(), schemaName, sourceTableName, targetTableName, System.currentTimeMillis() - startTimeMillis);
86 return result.toString();
87 }
88
89 private Optional<String> decorate(final DatabaseType databaseType, final DataSource dataSource, final String schemaName, final String targetTableName,
90 final SQLParserEngine parserEngine, final String sql) throws SQLException {
91 if (Strings.isNullOrEmpty(sql)) {
92 return Optional.empty();
93 }
94 String databaseName;
95 try (Connection connection = dataSource.getConnection()) {
96 databaseName = connection.getCatalog();
97 }
98 String result = decorateActualSQL(databaseName, targetTableName, parserEngine, sql.trim());
99
100 if ("openGauss".equals(databaseType.getType())) {
101 return decorateOpenGauss(databaseName, schemaName, result, parserEngine);
102 }
103 return Optional.of(result);
104 }
105
106 private String decorateActualSQL(final String databaseName, final String targetTableName, final SQLParserEngine parserEngine, final String sql) {
107 QueryContext queryContext = getQueryContext(databaseName, parserEngine, sql);
108 SQLStatementContext sqlStatementContext = queryContext.getSqlStatementContext();
109 Map<SQLSegment, String> replaceMap = new TreeMap<>(Comparator.comparing(SQLSegment::getStartIndex));
110 if (sqlStatementContext instanceof CreateTableStatementContext) {
111 appendFromIndexAndConstraint(replaceMap, targetTableName, sqlStatementContext);
112 appendFromTable(replaceMap, targetTableName, (TableAvailable) sqlStatementContext);
113 }
114 if (sqlStatementContext instanceof CommentStatementContext) {
115 appendFromTable(replaceMap, targetTableName, (TableAvailable) sqlStatementContext);
116 }
117 if (sqlStatementContext instanceof CreateIndexStatementContext) {
118 appendFromTable(replaceMap, targetTableName, (TableAvailable) sqlStatementContext);
119 appendFromIndexAndConstraint(replaceMap, targetTableName, sqlStatementContext);
120 }
121 if (sqlStatementContext instanceof AlterTableStatementContext) {
122 appendFromIndexAndConstraint(replaceMap, targetTableName, sqlStatementContext);
123 appendFromTable(replaceMap, targetTableName, (TableAvailable) sqlStatementContext);
124 }
125 return doDecorateActualTable(replaceMap, sql);
126 }
127
128 private QueryContext getQueryContext(final String databaseName, final SQLParserEngine parserEngine, final String sql) {
129 SQLStatementContext sqlStatementContext = new SQLBindEngine(null, databaseName, new HintValueContext()).bind(parserEngine.parse(sql, false), Collections.emptyList());
130 return new QueryContext(sqlStatementContext, sql, Collections.emptyList(), new HintValueContext());
131 }
132
133 private void appendFromIndexAndConstraint(final Map<SQLSegment, String> replaceMap, final String targetTableName, final SQLStatementContext sqlStatementContext) {
134 if (!(sqlStatementContext instanceof TableAvailable) || ((TableAvailable) sqlStatementContext).getTablesContext().getSimpleTableSegments().isEmpty()) {
135 return;
136 }
137 TableNameSegment tableNameSegment = ((TableAvailable) sqlStatementContext).getTablesContext().getSimpleTableSegments().iterator().next().getTableName();
138 if (!tableNameSegment.getIdentifier().getValue().equals(targetTableName)) {
139 if (sqlStatementContext instanceof IndexAvailable) {
140 for (IndexSegment each : ((IndexAvailable) sqlStatementContext).getIndexes()) {
141 String logicIndexName = IndexMetaDataUtils.getLogicIndexName(each.getIndexName().getIdentifier().getValue(), tableNameSegment.getIdentifier().getValue());
142 replaceMap.put(each.getIndexName(), logicIndexName);
143 }
144 }
145 if (sqlStatementContext instanceof ConstraintAvailable) {
146 for (ConstraintSegment each : ((ConstraintAvailable) sqlStatementContext).getConstraints()) {
147 String logicConstraint = IndexMetaDataUtils.getLogicIndexName(each.getIdentifier().getValue(), tableNameSegment.getIdentifier().getValue());
148 replaceMap.put(each, logicConstraint);
149 }
150 }
151 }
152 }
153
154 private void appendFromTable(final Map<SQLSegment, String> replaceMap, final String targetTableName, final TableAvailable sqlStatementContext) {
155 for (SimpleTableSegment each : sqlStatementContext.getAllTables()) {
156 if (!targetTableName.equals(each.getTableName().getIdentifier().getValue())) {
157 replaceMap.put(each.getTableName(), targetTableName);
158 }
159 }
160 }
161
162 private String doDecorateActualTable(final Map<SQLSegment, String> replaceMap, final String sql) {
163 StringBuilder result = new StringBuilder();
164 int lastStopIndex = 0;
165 for (Entry<SQLSegment, String> entry : replaceMap.entrySet()) {
166 result.append(sql, lastStopIndex, entry.getKey().getStartIndex());
167 result.append(entry.getValue());
168 lastStopIndex = entry.getKey().getStopIndex() + 1;
169 }
170 if (lastStopIndex < sql.length()) {
171 result.append(sql, lastStopIndex, sql.length());
172 }
173 return result.toString();
174 }
175
176
177 private Optional<String> decorateOpenGauss(final String databaseName, final String schemaName, final String queryContext,
178 final SQLParserEngine parserEngine) {
179 if (queryContext.toLowerCase().startsWith(SET_SEARCH_PATH_PREFIX)) {
180 return Optional.empty();
181 }
182 return Optional.of(replaceTableNameWithPrefix(queryContext, schemaName + ".", databaseName, parserEngine));
183 }
184
185 private String replaceTableNameWithPrefix(final String sql, final String prefix, final String databaseName, final SQLParserEngine parserEngine) {
186 QueryContext queryContext = getQueryContext(databaseName, parserEngine, sql);
187 SQLStatementContext sqlStatementContext = queryContext.getSqlStatementContext();
188 if (sqlStatementContext instanceof CreateTableStatementContext || sqlStatementContext instanceof CommentStatementContext
189 || sqlStatementContext instanceof CreateIndexStatementContext || sqlStatementContext instanceof AlterTableStatementContext) {
190 if (sqlStatementContext.getTablesContext().getSimpleTableSegments().isEmpty()) {
191 return sql;
192 }
193 if (sqlStatementContext.getTablesContext().getSchemaName().isPresent()) {
194 return sql;
195 }
196 Map<SQLSegment, String> replaceMap = new TreeMap<>(Comparator.comparing(SQLSegment::getStartIndex));
197 TableNameSegment tableNameSegment = sqlStatementContext.getTablesContext().getSimpleTableSegments().iterator().next().getTableName();
198 replaceMap.put(tableNameSegment, prefix + tableNameSegment.getIdentifier().getValue());
199 return doDecorateActualTable(replaceMap, sql);
200 }
201 return sql;
202 }
203 }