[improvement][fix](insert)(replay) support SHOW LAST INSERT stmt and fix json replay bug (#8355)

1. support SHOW LAST INSERT
    In the current implementation, the insert operation returns a json string to describe the result information
    of the insert. But this information is in the session track field of the mysql protocol,
    and it is difficult to obtain programmatically.

    Therefore, I provide a new syntax `show last insert` to explicitly obtain the result of the latest insert operation,
    and return a normal query result set to facilitate the user to obtain the result information of the insert.

2. the `ReturnRows` field in fe.audit.log of insert operation will be set to the loaded row num of the insert.

3.  Fix a bug described in #8354
This commit is contained in:
Mingyu Chen
2022-03-08 18:53:11 +08:00
committed by GitHub
parent b9010e34d0
commit 1e70f992e7
13 changed files with 355 additions and 4 deletions

View File

@ -2876,6 +2876,11 @@ show_param ::=
{:
RESULT = new ShowTableCreationStmt(db, parser.wild);
:}
/* show last insert */
| KW_LAST KW_INSERT
{:
RESULT = new ShowLastInsertStmt();
:}
;
opt_tmp ::=

View File

@ -0,0 +1,58 @@
// 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.doris.analysis;
import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.ScalarType;
import org.apache.doris.qe.ShowResultSetMetaData;
// SHOW LAST INSERT
public class ShowLastInsertStmt extends ShowStmt {
private static final ShowResultSetMetaData META_DATA =
ShowResultSetMetaData.builder()
.addColumn(new Column("TransactionId", ScalarType.createVarchar(128)))
.addColumn(new Column("Label", ScalarType.createVarchar(128)))
.addColumn(new Column("Database", ScalarType.createVarchar(128)))
.addColumn(new Column("Table", ScalarType.createVarchar(128)))
.addColumn(new Column("TransactionStatus", ScalarType.createVarchar(64)))
.addColumn(new Column("LoadedRows", ScalarType.createVarchar(128)))
.addColumn(new Column("FilteredRows", ScalarType.createVarchar(128)))
.build();
@Override
public void analyze(Analyzer analyzer) {
// No need to check priv here. Bacause `show last insert` can only view
// the insert result of current session.
// So if user does not have priv to insert, than there is no result to show.
}
@Override
public String toSql() {
return "SHOW INSERT RESULT";
}
@Override
public String toString() {
return toSql();
}
@Override
public ShowResultSetMetaData getMetaData() {
return META_DATA;
}
}

View File

@ -33,6 +33,7 @@ import org.apache.doris.resource.Tag;
import org.apache.doris.thrift.TResourceInfo;
import org.apache.doris.thrift.TUniqueId;
import org.apache.doris.transaction.TransactionEntry;
import org.apache.doris.transaction.TransactionStatus;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@ -134,6 +135,21 @@ public class ConnectContext {
// The FE ip current connected
private String currentConnectedFEIp = "";
private InsertResult insertResult;
public void setOrUpdateInsertResult(long txnId, String label, String db, String tbl,
TransactionStatus txnStatus, long loadedRows, int filteredRows) {
if (isTxnModel() && insertResult != null) {
insertResult.updateResult(txnStatus, loadedRows, filteredRows);
} else {
insertResult = new InsertResult(txnId, label, db, tbl, txnStatus, loadedRows, filteredRows);
}
}
public InsertResult getInsertResult() {
return insertResult;
}
public static ConnectContext get() {
return threadLocalInfo.get();
}
@ -544,4 +560,5 @@ public class ConnectContext {
public String getQueryIdentifier() {
return "stmt[" + stmtId + ", " + DebugUtil.printId(queryId) + "]";
}
}

View File

@ -0,0 +1,66 @@
// 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.doris.qe;
import org.apache.doris.transaction.TransactionStatus;
import com.google.common.collect.Lists;
import java.util.List;
// Save the result of last insert operation.
// So that user can view it by executing SHOW LAST INSERT.
public class InsertResult {
public long txnId;
public String label;
public String db;
public String tbl;
public TransactionStatus txnStatus;
public long loadedRows;
public long filteredRows;
public InsertResult(long txnId, String label, String db, String tbl, TransactionStatus txnStatus,
long loadedRows, long filteredRows) {
this.txnId = txnId;
this.label = label;
this.db = db;
this.tbl = tbl;
this.txnStatus = txnStatus;
this.loadedRows = loadedRows;
this.filteredRows = filteredRows;
}
public void updateResult(TransactionStatus txnStatus, long loadedRows, int filteredRows) {
this.txnStatus = txnStatus;
this.loadedRows += loadedRows;
this.filteredRows += filteredRows;
}
public List<String> toRow() {
List<String> row = Lists.newArrayList();
row.add(String.valueOf(txnId));
row.add(label);
row.add(db);
row.add(tbl);
row.add(txnStatus.name());
row.add(String.valueOf(loadedRows));
row.add(String.valueOf(filteredRows));
return row;
}
}

View File

@ -1000,10 +1000,11 @@ public class SessionVariable implements Serializable, Writable {
field.set(this, root.get(attr.name()));
break;
case "int":
field.set(this, root.get(attr.name()));
// root.get(attr.name()) always return Long type, so need to convert it.
field.set(this, Integer.valueOf(root.get(attr.name()).toString()));
break;
case "long":
field.set(this, root.get(attr.name()));
field.set(this, (Long) root.get(attr.name()));
break;
case "float":
field.set(this, root.get(attr.name()));
@ -1107,3 +1108,4 @@ public class SessionVariable implements Serializable, Writable {
}
}
}

View File

@ -49,6 +49,7 @@ import org.apache.doris.analysis.ShowFrontendsStmt;
import org.apache.doris.analysis.ShowFunctionsStmt;
import org.apache.doris.analysis.ShowGrantsStmt;
import org.apache.doris.analysis.ShowIndexStmt;
import org.apache.doris.analysis.ShowLastInsertStmt;
import org.apache.doris.analysis.ShowLoadProfileStmt;
import org.apache.doris.analysis.ShowLoadStmt;
import org.apache.doris.analysis.ShowLoadWarningsStmt;
@ -157,6 +158,7 @@ import org.apache.doris.system.Backend;
import org.apache.doris.system.SystemInfoService;
import org.apache.doris.thrift.TUnit;
import org.apache.doris.transaction.GlobalTransactionMgr;
import org.apache.doris.transaction.TransactionStatus;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
@ -165,7 +167,6 @@ import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.tuple.Triple;
import org.apache.doris.transaction.TransactionStatus;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -330,6 +331,8 @@ public class ShowExecutor {
handleShowColumnStats();
} else if (stmt instanceof ShowTableCreationStmt) {
handleShowTableCreation();
} else if (stmt instanceof ShowLastInsertStmt) {
handleShowLastInsert();
} else {
handleEmtpy();
}
@ -2108,4 +2111,17 @@ public class ShowExecutor {
resultSet = new ShowResultSet(showMetaData, resultRowSet);
}
private void handleShowLastInsert() {
ShowLastInsertStmt showStmt = (ShowLastInsertStmt) stmt;
List<List<String>> resultRowSet = Lists.newArrayList();
if (ConnectContext.get() != null) {
InsertResult insertResult = ConnectContext.get().getInsertResult();
if (insertResult != null) {
resultRowSet.add(insertResult.toRow());
}
}
ShowResultSetMetaData showMetaData = showStmt.getMetaData();
resultSet = new ShowResultSet(showMetaData, resultRowSet);
}
}

View File

@ -1375,6 +1375,13 @@ public class StmtExecutor implements ProfileWriter {
sb.append("}");
context.getState().setOk(loadedRows, filteredRows, sb.toString());
// set insert result in connection context,
// so that user can use `show insert result` to get info of the last insert operation.
context.setOrUpdateInsertResult(txnId, label, insertStmt.getDb(), insertStmt.getTbl(),
txnStatus, loadedRows, filteredRows);
// update it, so that user can get loaded rows in fe.audit.log
context.updateReturnRows((int) loadedRows);
}
private void handleUnsupportedStmt() {