1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.shardingsphere.driver.jdbc.core.statement;
19
20 import lombok.AccessLevel;
21 import lombok.Getter;
22 import org.apache.shardingsphere.database.connector.core.metadata.database.metadata.option.keygen.DialectGeneratedKeyOption;
23 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
24 import org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry;
25 import org.apache.shardingsphere.database.exception.core.SQLExceptionTransformEngine;
26 import org.apache.shardingsphere.driver.executor.callback.add.StatementAddCallback;
27 import org.apache.shardingsphere.driver.executor.engine.batch.preparedstatement.DriverExecuteBatchExecutor;
28 import org.apache.shardingsphere.driver.executor.engine.facade.DriverExecutorFacade;
29 import org.apache.shardingsphere.driver.jdbc.adapter.AbstractPreparedStatementAdapter;
30 import org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection;
31 import org.apache.shardingsphere.driver.jdbc.core.resultset.GeneratedKeysResultSet;
32 import org.apache.shardingsphere.driver.jdbc.core.resultset.ShardingSphereResultSet;
33 import org.apache.shardingsphere.driver.jdbc.core.statement.metadata.ShardingSphereParameterMetaData;
34 import org.apache.shardingsphere.infra.annotation.HighFrequencyInvocation;
35 import org.apache.shardingsphere.infra.binder.context.aware.ParameterAware;
36 import org.apache.shardingsphere.infra.binder.context.segment.insert.keygen.GeneratedKeyContext;
37 import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
38 import org.apache.shardingsphere.infra.binder.context.statement.type.dml.InsertStatementContext;
39 import org.apache.shardingsphere.infra.binder.engine.SQLBindEngine;
40 import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
41 import org.apache.shardingsphere.infra.exception.kernel.syntax.EmptySQLException;
42 import org.apache.shardingsphere.infra.executor.sql.prepare.driver.jdbc.JDBCDriverType;
43 import org.apache.shardingsphere.infra.executor.sql.prepare.driver.jdbc.StatementOption;
44 import org.apache.shardingsphere.infra.hint.HintManager;
45 import org.apache.shardingsphere.infra.hint.HintValueContext;
46 import org.apache.shardingsphere.infra.hint.SQLHintUtils;
47 import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
48 import org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
49 import org.apache.shardingsphere.infra.rule.attribute.datanode.DataNodeRuleAttribute;
50 import org.apache.shardingsphere.infra.rule.attribute.resoure.StorageConnectorReusableRuleAttribute;
51 import org.apache.shardingsphere.infra.session.query.QueryContext;
52 import org.apache.shardingsphere.parser.rule.SQLParserRule;
53 import org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
54
55 import java.sql.ParameterMetaData;
56 import java.sql.PreparedStatement;
57 import java.sql.ResultSet;
58 import java.sql.SQLException;
59 import java.sql.Statement;
60 import java.util.ArrayList;
61 import java.util.Collection;
62 import java.util.Collections;
63 import java.util.LinkedList;
64 import java.util.List;
65 import java.util.Map;
66 import java.util.Optional;
67
68
69
70
71 @HighFrequencyInvocation
72 public final class ShardingSpherePreparedStatement extends AbstractPreparedStatementAdapter {
73
74 @Getter
75 private final ShardingSphereConnection connection;
76
77 private final ShardingSphereMetaData metaData;
78
79 private final String sql;
80
81 private final HintValueContext hintValueContext;
82
83 private final SQLStatementContext sqlStatementContext;
84
85 private final ShardingSphereDatabase usedDatabase;
86
87 private final StatementOption statementOption;
88
89 @Getter(AccessLevel.PROTECTED)
90 private final StatementManager statementManager;
91
92 @Getter
93 private final ParameterMetaData parameterMetaData;
94
95 private final DriverExecutorFacade driverExecutorFacade;
96
97 private final DriverExecuteBatchExecutor executeBatchExecutor;
98
99 private final List<PreparedStatement> statements = new ArrayList<>();
100
101 private final List<List<Object>> parameterSets = new ArrayList<>();
102
103 private final Collection<Comparable<?>> generatedValues = new LinkedList<>();
104
105 private final boolean statementsCacheable;
106
107 private Map<String, Integer> columnLabelAndIndexMap;
108
109 private ResultSet currentResultSet;
110
111 private ResultSet currentBatchGeneratedKeysResultSet;
112
113 private QueryContext queryContext;
114
115 public ShardingSpherePreparedStatement(final ShardingSphereConnection connection, final String sql) throws SQLException {
116 this(connection, sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT, false, null);
117 }
118
119 public ShardingSpherePreparedStatement(final ShardingSphereConnection connection, final String sql, final int resultSetType, final int resultSetConcurrency) throws SQLException {
120 this(connection, sql, resultSetType, resultSetConcurrency, ResultSet.HOLD_CURSORS_OVER_COMMIT, false, null);
121 }
122
123 public ShardingSpherePreparedStatement(final ShardingSphereConnection connection, final String sql, final int autoGeneratedKeys) throws SQLException {
124 this(connection, sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT, RETURN_GENERATED_KEYS == autoGeneratedKeys, null);
125 }
126
127 public ShardingSpherePreparedStatement(final ShardingSphereConnection connection, final String sql, final String[] columns) throws SQLException {
128 this(connection, sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT, true, columns);
129 }
130
131 public ShardingSpherePreparedStatement(final ShardingSphereConnection connection, final String sql, final int resultSetType, final int resultSetConcurrency,
132 final int resultSetHoldability) throws SQLException {
133 this(connection, sql, resultSetType, resultSetConcurrency, resultSetHoldability, false, null);
134 }
135
136 private ShardingSpherePreparedStatement(final ShardingSphereConnection connection, final String originSQL, final int resultSetType, final int resultSetConcurrency,
137 final int resultSetHoldability, final boolean returnGeneratedKeys, final String[] columns) throws SQLException {
138 ShardingSpherePreconditions.checkNotEmpty(originSQL, () -> new EmptySQLException().toSQLException());
139 this.connection = connection;
140 metaData = connection.getContextManager().getMetaDataContexts().getMetaData();
141 sql = SQLHintUtils.removeHint(originSQL);
142 hintValueContext = SQLHintUtils.extractHint(originSQL);
143 DatabaseType databaseType = metaData.getDatabase(connection.getCurrentDatabaseName()).getProtocolType();
144 SQLStatement sqlStatement = metaData.getGlobalRuleMetaData().getSingleRule(SQLParserRule.class).getSQLParserEngine(databaseType).parse(sql, true);
145 sqlStatementContext = new SQLBindEngine(metaData, connection.getCurrentDatabaseName(), hintValueContext).bind(sqlStatement);
146 String usedDatabaseName = sqlStatementContext.getTablesContext().getDatabaseName().orElse(connection.getCurrentDatabaseName());
147 connection.getDatabaseConnectionManager().getConnectionContext().setCurrentDatabaseName(connection.getCurrentDatabaseName());
148 usedDatabase = metaData.getDatabase(usedDatabaseName);
149 statementOption = returnGeneratedKeys ? new StatementOption(true, columns) : new StatementOption(resultSetType, resultSetConcurrency, resultSetHoldability);
150 statementManager = new StatementManager();
151 connection.getStatementManagers().add(statementManager);
152 parameterMetaData = new ShardingSphereParameterMetaData(sqlStatement);
153 driverExecutorFacade = new DriverExecutorFacade(connection, statementOption, statementManager, JDBCDriverType.PREPARED_STATEMENT);
154 executeBatchExecutor = new DriverExecuteBatchExecutor(connection, metaData, statementOption, statementManager, usedDatabase);
155 statementsCacheable = isStatementsCacheable();
156 }
157
158 private boolean isStatementsCacheable() {
159 return usedDatabase.getRuleMetaData().getAttributes(StorageConnectorReusableRuleAttribute.class).size() == usedDatabase.getRuleMetaData().getRules().size()
160 && !HintManager.isInstantiated();
161 }
162
163 @Override
164 public ResultSet executeQuery() throws SQLException {
165 try {
166 if (statementsCacheable && !statements.isEmpty()) {
167 resetParameters();
168 return statements.iterator().next().executeQuery();
169 }
170 clearPrevious();
171 QueryContext queryContext = createQueryContext();
172 this.queryContext = queryContext;
173 handleAutoCommitBeforeExecution(queryContext.getSqlStatementContext().getSqlStatement(), connection);
174 findGeneratedKey().ifPresent(optional -> generatedValues.addAll(optional.getGeneratedValues()));
175 currentResultSet =
176 driverExecutorFacade.executeQuery(usedDatabase, metaData, queryContext, this, columnLabelAndIndexMap, (StatementAddCallback<PreparedStatement>) this::addStatements, this::replay);
177 if (currentResultSet instanceof ShardingSphereResultSet) {
178 columnLabelAndIndexMap = ((ShardingSphereResultSet) currentResultSet).getColumnLabelAndIndexMap();
179 }
180 return currentResultSet;
181
182 } catch (final RuntimeException | SQLException ex) {
183
184 handleExceptionInTransaction(connection, metaData);
185 throw SQLExceptionTransformEngine.toSQLException(ex, usedDatabase.getProtocolType());
186 } finally {
187 executeBatchExecutor.clear();
188 clearParameters();
189 }
190 }
191
192 private void addStatements(final Collection<PreparedStatement> statements, final Collection<List<Object>> parameterSets) {
193 this.statements.addAll(statements);
194 this.parameterSets.addAll(parameterSets);
195 }
196
197 private void resetParameters() throws SQLException {
198 replaySetParameter(statements, Collections.singletonList(getParameters()));
199 }
200
201 @Override
202 public int executeUpdate() throws SQLException {
203 try {
204 if (statementsCacheable && !statements.isEmpty()) {
205 resetParameters();
206 return statements.iterator().next().executeUpdate();
207 }
208 clearPrevious();
209 QueryContext queryContext = createQueryContext();
210 this.queryContext = queryContext;
211 handleAutoCommitBeforeExecution(queryContext.getSqlStatementContext().getSqlStatement(), connection);
212 int result = driverExecutorFacade.executeUpdate(usedDatabase, metaData, queryContext,
213 (sql, statement) -> ((PreparedStatement) statement).executeUpdate(), (StatementAddCallback<PreparedStatement>) this::addStatements, this::replay);
214 findGeneratedKey().ifPresent(optional -> generatedValues.addAll(optional.getGeneratedValues()));
215 return result;
216
217 } catch (final RuntimeException | SQLException ex) {
218
219 handleExceptionInTransaction(connection, metaData);
220 throw SQLExceptionTransformEngine.toSQLException(ex, usedDatabase.getProtocolType());
221 } finally {
222 clearBatch();
223 }
224 }
225
226 @Override
227 public boolean execute() throws SQLException {
228 try {
229 if (statementsCacheable && !statements.isEmpty()) {
230 resetParameters();
231 return statements.iterator().next().execute();
232 }
233 clearPrevious();
234 QueryContext queryContext = createQueryContext();
235 this.queryContext = queryContext;
236 handleAutoCommitBeforeExecution(queryContext.getSqlStatementContext().getSqlStatement(), connection);
237 boolean result = driverExecutorFacade.execute(usedDatabase, metaData, queryContext, (sql, statement) -> ((PreparedStatement) statement).execute(),
238 (StatementAddCallback<PreparedStatement>) this::addStatements, this::replay);
239 findGeneratedKey().ifPresent(optional -> generatedValues.addAll(optional.getGeneratedValues()));
240 return result;
241
242 } catch (final RuntimeException | SQLException ex) {
243
244 handleExceptionInTransaction(connection, metaData);
245 throw SQLExceptionTransformEngine.toSQLException(ex, usedDatabase.getProtocolType());
246 } finally {
247 clearBatch();
248 }
249 }
250
251 @Override
252 public ResultSet getResultSet() throws SQLException {
253 if (null != currentResultSet) {
254 return currentResultSet;
255 }
256 driverExecutorFacade.getResultSet(usedDatabase, queryContext, this, statements).ifPresent(optional -> currentResultSet = optional);
257 if (null == columnLabelAndIndexMap && currentResultSet instanceof ShardingSphereResultSet) {
258 columnLabelAndIndexMap = ((ShardingSphereResultSet) currentResultSet).getColumnLabelAndIndexMap();
259 }
260 return currentResultSet;
261 }
262
263 private QueryContext createQueryContext() {
264 List<Object> params = new ArrayList<>(getParameters());
265 if (sqlStatementContext instanceof ParameterAware) {
266 ((ParameterAware) sqlStatementContext).bindParameters(params);
267 }
268 return new QueryContext(sqlStatementContext, sql, params, hintValueContext, connection.getDatabaseConnectionManager().getConnectionContext(), metaData, true);
269 }
270
271 private void replay() throws SQLException {
272 replaySetParameter(statements, parameterSets);
273 for (Statement each : statements) {
274 getMethodInvocationRecorder().replay(each);
275 }
276 }
277
278 private void replaySetParameter(final List<PreparedStatement> statements, final List<List<Object>> parameterSets) throws SQLException {
279 for (int i = 0; i < statements.size(); i++) {
280 replaySetParameter(statements.get(i), parameterSets.get(i));
281 }
282 }
283
284 private void clearPrevious() {
285 currentResultSet = null;
286 statements.clear();
287 parameterSets.clear();
288 generatedValues.clear();
289 }
290
291 private Optional<GeneratedKeyContext> findGeneratedKey() {
292 return sqlStatementContext instanceof InsertStatementContext ? ((InsertStatementContext) sqlStatementContext).getGeneratedKeyContext() : Optional.empty();
293 }
294
295 @Override
296 public ResultSet getGeneratedKeys() throws SQLException {
297 if (null != currentBatchGeneratedKeysResultSet) {
298 return currentBatchGeneratedKeysResultSet;
299 }
300 Optional<GeneratedKeyContext> generatedKey = findGeneratedKey();
301 if (generatedKey.isPresent() && statementOption.isReturnGeneratedKeys() && !generatedValues.isEmpty()) {
302 return new GeneratedKeysResultSet(getGeneratedKeysColumnName(generatedKey.get().getColumnName()), generatedValues.iterator(), this);
303 }
304 for (PreparedStatement each : statements) {
305 ResultSet resultSet = each.getGeneratedKeys();
306 while (resultSet.next()) {
307 generatedValues.add((Comparable<?>) resultSet.getObject(1));
308 }
309 }
310 String columnName = generatedKey.map(GeneratedKeyContext::getColumnName).orElse(null);
311 return new GeneratedKeysResultSet(getGeneratedKeysColumnName(columnName), generatedValues.iterator(), this);
312 }
313
314 private String getGeneratedKeysColumnName(final String columnName) {
315 Optional<DialectGeneratedKeyOption> generatedKeyOption = new DatabaseTypeRegistry(usedDatabase.getProtocolType()).getDialectDatabaseMetaData().getGeneratedKeyOption();
316 return generatedKeyOption.isPresent() ? generatedKeyOption.get().getColumnName() : columnName;
317 }
318
319 @Override
320 public void addBatch() {
321 currentResultSet = null;
322 QueryContext queryContext = createQueryContext();
323 this.queryContext = queryContext;
324 executeBatchExecutor.addBatch(queryContext, usedDatabase);
325 findGeneratedKey().ifPresent(optional -> generatedValues.addAll(optional.getGeneratedValues()));
326 clearParameters();
327 }
328
329 @Override
330 public int[] executeBatch() throws SQLException {
331 try {
332 return executeBatchExecutor.executeBatch(usedDatabase, sqlStatementContext, generatedValues, statementOption,
333 (StatementAddCallback<PreparedStatement>) (statements, parameterSets) -> this.statements.addAll(statements),
334 this::replaySetParameter,
335 () -> {
336 currentBatchGeneratedKeysResultSet = getGeneratedKeys();
337 statements.clear();
338 });
339
340 } catch (final RuntimeException ex) {
341
342 handleExceptionInTransaction(connection, metaData);
343 throw SQLExceptionTransformEngine.toSQLException(ex, usedDatabase.getProtocolType());
344 } finally {
345 clearBatch();
346 }
347 }
348
349 @Override
350 public void clearBatch() {
351 currentResultSet = null;
352 closeCurrentBatchGeneratedKeysResultSet();
353 executeBatchExecutor.clear();
354 clearParameters();
355 }
356
357 private void closeCurrentBatchGeneratedKeysResultSet() {
358 if (null != currentBatchGeneratedKeysResultSet) {
359 try {
360 currentBatchGeneratedKeysResultSet.close();
361 } catch (final SQLException ignored) {
362 } finally {
363 currentBatchGeneratedKeysResultSet = null;
364 }
365 }
366 }
367
368 @SuppressWarnings("MagicConstant")
369 @Override
370 public int getResultSetType() {
371 return statementOption.getResultSetType();
372 }
373
374 @SuppressWarnings("MagicConstant")
375 @Override
376 public int getResultSetConcurrency() {
377 return statementOption.getResultSetConcurrency();
378 }
379
380 @Override
381 public int getResultSetHoldability() {
382 return statementOption.getResultSetHoldability();
383 }
384
385 @Override
386 public boolean isAccumulate() {
387 for (DataNodeRuleAttribute each : usedDatabase.getRuleMetaData().getAttributes(DataNodeRuleAttribute.class)) {
388 if (each.isNeedAccumulate(sqlStatementContext.getTablesContext().getTableNames())) {
389 return true;
390 }
391 }
392 return false;
393 }
394
395 @Override
396 public Collection<PreparedStatement> getRoutedStatements() {
397 return statements;
398 }
399
400 @Override
401 protected void closeExecutor() throws SQLException {
402 driverExecutorFacade.close();
403 }
404 }