Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ literals
| bitValueLiterals
| booleanLiterals
| nullValueLiterals
| arrayLiterals
;

string_
Expand Down Expand Up @@ -110,6 +111,10 @@ nullValueLiterals
: NULL
;

arrayLiterals
: LBT_ (expr (COMMA_ expr)*)? RBT_
;

collationName
: textOrIdentifier | BINARY
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,10 @@ showCharset
: SHOW CHARSET
;

showTransaction
: SHOW TRANSACTION fromDatabase? showWhereClause?
;

setCharacter
: SET (CHARACTER SET | CHARSET) (charsetName | DEFAULT)
;
Expand Down Expand Up @@ -707,4 +711,5 @@ show
| showVariables
| showReplicas
| showReplicaStatus
| showTransaction
;
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.apache.shardingsphere.sql.parser.autogen.DorisStatementBaseVisitor;
import org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.AggregationFunctionContext;
import org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.AliasContext;
import org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.ArrayLiteralsContext;
import org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.AssignmentContext;
import org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.AssignmentValueContext;
import org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.AssignmentValuesContext;
Expand Down Expand Up @@ -290,7 +291,10 @@ public final ASTNode visitLiterals(final LiteralsContext ctx) {
if (null != ctx.nullValueLiterals()) {
return visit(ctx.nullValueLiterals());
}
throw new IllegalStateException("Literals must have string, number, dateTime, hex, bit, boolean or null.");
if (null != ctx.arrayLiterals()) {
return visit(ctx.arrayLiterals());
}
throw new IllegalStateException("Literals must have string, number, dateTime, hex, bit, boolean, null or array.");
}

@Override
Expand Down Expand Up @@ -336,6 +340,15 @@ public final ASTNode visitNullValueLiterals(final NullValueLiteralsContext ctx)
return new NullLiteralValue(ctx.getText());
}

@Override
public final ASTNode visitArrayLiterals(final ArrayLiteralsContext ctx) {
ListExpression result = new ListExpression(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex());
for (ExprContext each : ctx.expr()) {
result.getItems().add((ExpressionSegment) visit(each));
}
return result;
}

@Override
public final ASTNode visitIdentifier(final IdentifierContext ctx) {
return new IdentifierValue(ctx.getText());
Expand Down Expand Up @@ -622,7 +635,11 @@ public final ASTNode visitSimpleExpr(final SimpleExprContext ctx) {
return result;
}
if (null != ctx.literals()) {
return SQLUtils.createLiteralExpression(visit(ctx.literals()), startIndex, stopIndex, ctx.literals().start.getInputStream().getText(new Interval(startIndex, stopIndex)));
ASTNode astNode = visit(ctx.literals());
if (astNode instanceof ExpressionSegment) {
return astNode;
}
return SQLUtils.createLiteralExpression(astNode, startIndex, stopIndex, ctx.literals().start.getInputStream().getText(new Interval(startIndex, stopIndex)));
}
if (null != ctx.intervalExpression()) {
return visit(ctx.intervalExpression());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
import org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.ShowStatusContext;
import org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.ShowTableStatusContext;
import org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.ShowTablesContext;
import org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.ShowTransactionContext;
import org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.ShowTriggersContext;
import org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.ShowVariablesContext;
import org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.ShowWarningsContext;
Expand Down Expand Up @@ -232,6 +233,7 @@
import org.apache.shardingsphere.sql.parser.statement.mysql.dal.show.process.MySQLShowProcessListStatement;
import org.apache.shardingsphere.sql.parser.statement.mysql.dal.show.profile.MySQLShowProfileStatement;
import org.apache.shardingsphere.sql.parser.statement.mysql.dal.show.profile.MySQLShowProfilesStatement;
import org.apache.shardingsphere.sql.parser.statement.mysql.dal.show.MySQLShowTransactionStatement;
import org.apache.shardingsphere.sql.parser.statement.mysql.dal.show.table.MySQLShowCreateTableStatement;
import org.apache.shardingsphere.sql.parser.statement.mysql.dal.show.table.MySQLShowOpenTablesStatement;
import org.apache.shardingsphere.sql.parser.statement.mysql.dal.show.table.MySQLShowTableStatusStatement;
Expand Down Expand Up @@ -815,6 +817,19 @@ public ASTNode visitShowPrivileges(final ShowPrivilegesContext ctx) {
return new MySQLShowPrivilegesStatement(getDatabaseType());
}

@Override
public ASTNode visitShowTransaction(final ShowTransactionContext ctx) {
MySQLShowTransactionStatement result = new MySQLShowTransactionStatement(getDatabaseType());
if (null != ctx.fromDatabase()) {
result.setFromDatabase((FromDatabaseSegment) visit(ctx.fromDatabase()));
}
if (null != ctx.showWhereClause()) {
result.setWhere((WhereSegment) visit(ctx.showWhereClause()));
}
result.addParameterMarkers(getParameterMarkerSegments());
return result;
}

@Override
public ASTNode visitShutdown(final ShutdownContext ctx) {
return new MySQLShutdownStatement(getDatabaseType());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.shardingsphere.sql.parser.statement.mysql.dal.show;

import lombok.Getter;
import lombok.Setter;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dal.FromDatabaseSegment;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.predicate.WhereSegment;
import org.apache.shardingsphere.sql.parser.statement.core.statement.attribute.SQLStatementAttributes;
import org.apache.shardingsphere.sql.parser.statement.core.statement.attribute.type.AllowNotUseDatabaseSQLStatementAttribute;
import org.apache.shardingsphere.sql.parser.statement.core.statement.attribute.type.DatabaseSelectRequiredSQLStatementAttribute;
import org.apache.shardingsphere.sql.parser.statement.core.statement.attribute.type.FromDatabaseSQLStatementAttribute;
import org.apache.shardingsphere.sql.parser.statement.core.statement.attribute.type.TablelessDataSourceBroadcastRouteSQLStatementAttribute;
import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dal.DALStatement;

/**
* Show transaction statement for MySQL.
*/
@Getter
@Setter
public final class MySQLShowTransactionStatement extends DALStatement {

private FromDatabaseSegment fromDatabase;

private WhereSegment where;

public MySQLShowTransactionStatement(final DatabaseType databaseType) {
super(databaseType);
}

@Override
public SQLStatementAttributes getAttributes() {
String databaseName = null == fromDatabase ? null : fromDatabase.getDatabase().getIdentifier().getValue();
return new SQLStatementAttributes(new DatabaseSelectRequiredSQLStatementAttribute(), new FromDatabaseSQLStatementAttribute(fromDatabase),
new TablelessDataSourceBroadcastRouteSQLStatementAttribute(), new AllowNotUseDatabaseSQLStatementAttribute(true, databaseName));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.show;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.apache.shardingsphere.database.exception.core.exception.syntax.database.UnknownDatabaseException;
import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
import org.apache.shardingsphere.infra.executor.sql.execute.result.query.QueryResultMetaData;
import org.apache.shardingsphere.infra.executor.sql.execute.result.query.impl.raw.metadata.RawQueryResultColumnMetaData;
import org.apache.shardingsphere.infra.executor.sql.execute.result.query.impl.raw.metadata.RawQueryResultMetaData;
import org.apache.shardingsphere.infra.merge.result.MergedResult;
import org.apache.shardingsphere.infra.merge.result.impl.local.LocalDataMergedResult;
import org.apache.shardingsphere.infra.merge.result.impl.local.LocalDataQueryResultRow;
import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
import org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
import org.apache.shardingsphere.proxy.backend.handler.admin.executor.DatabaseAdminQueryExecutor;
import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.column.ColumnSegment;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.BinaryOperationExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.simple.LiteralExpressionSegment;
import org.apache.shardingsphere.sql.parser.statement.mysql.dal.show.MySQLShowTransactionStatement;

import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
* Show transaction executor for MySQL.
*/
@RequiredArgsConstructor
public class MySQLShowTransactionExecutor implements DatabaseAdminQueryExecutor {

private final MySQLShowTransactionStatement sqlStatement;

@Getter
private QueryResultMetaData queryResultMetaData;

@Getter
private MergedResult mergedResult;

private Optional<Long> filterTransactionId = Optional.empty();

private Optional<String> filterLabel = Optional.empty();

@Override
public void execute(final ConnectionSession connectionSession, final ShardingSphereMetaData metaData) {
String databaseName = getDatabaseName(connectionSession);
if (null != databaseName) {
ShardingSpherePreconditions.checkState(metaData.containsDatabase(databaseName), () -> new UnknownDatabaseException(databaseName));
}
queryResultMetaData = createQueryResultMetaData();
mergedResult = new LocalDataMergedResult(getQueryResultRows(databaseName, metaData));
}

private String getDatabaseName(final ConnectionSession connectionSession) {
return null == sqlStatement.getFromDatabase() ? connectionSession.getUsedDatabaseName() : sqlStatement.getFromDatabase().getDatabase().getIdentifier().getValue();
}

private QueryResultMetaData createQueryResultMetaData() {
List<RawQueryResultColumnMetaData> columns = new ArrayList<>(15);
columns.add(new RawQueryResultColumnMetaData("", "TransactionId", "TransactionId", Types.BIGINT, "BIGINT", 20, 0));
columns.add(new RawQueryResultColumnMetaData("", "Label", "Label", Types.VARCHAR, "VARCHAR", 255, 0));
columns.add(new RawQueryResultColumnMetaData("", "Coordinator", "Coordinator", Types.VARCHAR, "VARCHAR", 255, 0));
columns.add(new RawQueryResultColumnMetaData("", "TransactionStatus", "TransactionStatus", Types.VARCHAR, "VARCHAR", 50, 0));
columns.add(new RawQueryResultColumnMetaData("", "LoadJobSourceType", "LoadJobSourceType", Types.VARCHAR, "VARCHAR", 50, 0));
columns.add(new RawQueryResultColumnMetaData("", "PrepareTime", "PrepareTime", Types.VARCHAR, "VARCHAR", 50, 0));
columns.add(new RawQueryResultColumnMetaData("", "CommitTime", "CommitTime", Types.VARCHAR, "VARCHAR", 50, 0));
columns.add(new RawQueryResultColumnMetaData("", "FinishTime", "FinishTime", Types.VARCHAR, "VARCHAR", 50, 0));
columns.add(new RawQueryResultColumnMetaData("", "Reason", "Reason", Types.VARCHAR, "VARCHAR", 1000, 0));
columns.add(new RawQueryResultColumnMetaData("", "ErrorReplicasCount", "ErrorReplicasCount", Types.INTEGER, "INTEGER", 10, 0));
columns.add(new RawQueryResultColumnMetaData("", "ListenerId", "ListenerId", Types.BIGINT, "BIGINT", 20, 0));
columns.add(new RawQueryResultColumnMetaData("", "TimeoutMs", "TimeoutMs", Types.BIGINT, "BIGINT", 20, 0));
return new RawQueryResultMetaData(columns);
}

private Collection<LocalDataQueryResultRow> getQueryResultRows(final String databaseName, final ShardingSphereMetaData metaData) {
if (null == databaseName) {
return Collections.emptyList();
}
ShardingSphereDatabase database = metaData.getDatabase(databaseName);
if (!database.isComplete()) {
return Collections.emptyList();
}
extractWhereFilter();
Collection<TransactionInfo> transactions = loadTransactions();
return transactions.stream()
.filter(this::matchesFilter)
.map(this::buildTransactionRow)
.collect(Collectors.toList());
}

private void extractWhereFilter() {
ShardingSpherePreconditions.checkState(null != sqlStatement.getWhere(),
() -> new IllegalArgumentException("SHOW TRANSACTION requires WHERE clause with 'id = <transaction_id>' or 'label = <label_name>'"));
ShardingSpherePreconditions.checkState(sqlStatement.getWhere().getExpr() instanceof BinaryOperationExpression,
() -> new IllegalArgumentException("WHERE clause must be in the form: id = <value> or label = <value>"));
BinaryOperationExpression binaryExpr = (BinaryOperationExpression) sqlStatement.getWhere().getExpr();
extractFilterFromBinaryExpression(binaryExpr);
ShardingSpherePreconditions.checkState(filterTransactionId.isPresent() || filterLabel.isPresent(),
() -> new IllegalArgumentException("WHERE clause must specify either 'id = <transaction_id>' or 'label = <label_name>'"));
}

private void extractFilterFromBinaryExpression(final BinaryOperationExpression expression) {
ShardingSpherePreconditions.checkState(expression.getLeft() instanceof ColumnSegment && expression.getRight() instanceof LiteralExpressionSegment,
() -> new IllegalArgumentException("WHERE clause must be in the form: column = literal"));
ShardingSpherePreconditions.checkState("=".equals(expression.getOperator()),
() -> new IllegalArgumentException("WHERE clause only supports '=' operator, got: " + expression.getOperator()));
ColumnSegment column = (ColumnSegment) expression.getLeft();
LiteralExpressionSegment literal = (LiteralExpressionSegment) expression.getRight();
String columnName = column.getIdentifier().getValue().toLowerCase();
Object literalValue = literal.getLiterals();
if ("id".equalsIgnoreCase(columnName)) {
filterTransactionId = extractLongValue(literalValue);
ShardingSpherePreconditions.checkState(filterTransactionId.isPresent(),
() -> new IllegalArgumentException("Invalid transaction id value: " + literalValue));
} else if ("label".equalsIgnoreCase(columnName)) {
filterLabel = Optional.of(String.valueOf(literalValue));
} else {
throw new IllegalArgumentException("WHERE clause only supports 'id' or 'label' columns, got: " + columnName);
}
}

private Optional<Long> extractLongValue(final Object value) {
if (value instanceof Number) {
return Optional.of(((Number) value).longValue());
}
if (value instanceof String) {
try {
return Optional.of(Long.parseLong((String) value));
} catch (final NumberFormatException ignored) {
return Optional.empty();
}
}
return Optional.empty();
}

private boolean matchesFilter(final TransactionInfo transaction) {
if (filterTransactionId.isPresent() && filterTransactionId.get() != transaction.getTransactionId()) {
return false;
}
if (filterLabel.isPresent() && !filterLabel.get().equals(transaction.getLabel())) {
return false;
}
return true;
}

private LocalDataQueryResultRow buildTransactionRow(final TransactionInfo transaction) {
return new LocalDataQueryResultRow(
transaction.getTransactionId(),
transaction.getLabel(),
transaction.getCoordinator(),
transaction.getTransactionStatus(),
transaction.getLoadJobSourceType(),
transaction.getPrepareTime(),
transaction.getCommitTime(),
transaction.getFinishTime(),
transaction.getReason(),
transaction.getErrorReplicasCount(),
transaction.getListenerId(),
transaction.getTimeoutMs());
}

protected Collection<TransactionInfo> loadTransactions() {
throw new UnsupportedOperationException("SHOW TRANSACTION is not supported for the moment. ");
}

/**
* Transaction information holder.
*/
@Getter
@RequiredArgsConstructor
static class TransactionInfo {

private final long transactionId;

private final String label;

private final String coordinator;

private final String transactionStatus;

private final String loadJobSourceType;

private final String prepareTime;

private final String commitTime;

private final String finishTime;

private final String reason;

private final int errorReplicasCount;

private final long listenerId;

private final long timeoutMs;
}
}
Loading