func (sq *SqlQuery) ExecuteBatch(context *rpcproto.Context, queryList *proto.QueryList, reply *proto.QueryResultList) (err error) { defer handleError(&err, nil) if len(queryList.Queries) == 0 { panic(NewTabletError(FAIL, "Empty query list")) } sq.checkState(queryList.SessionId, false) begin_called := false var noOutput string session := proto.Session{ TransactionId: queryList.TransactionId, SessionId: queryList.SessionId, } reply.List = make([]mproto.QueryResult, 0, len(queryList.Queries)) for _, bound := range queryList.Queries { trimmed := strings.ToLower(strings.Trim(bound.Sql, " \t\r\n")) switch trimmed { case "begin": if session.TransactionId != 0 { panic(NewTabletError(FAIL, "Nested transactions disallowed")) } var txInfo proto.TransactionInfo if err = sq.Begin(context, &session, &txInfo); err != nil { return err } session.TransactionId = txInfo.TransactionId begin_called = true reply.List = append(reply.List, mproto.QueryResult{}) case "commit": if !begin_called { panic(NewTabletError(FAIL, "Cannot commit without begin")) } if err = sq.Commit(context, &session, &noOutput); err != nil { return err } session.TransactionId = 0 begin_called = false reply.List = append(reply.List, mproto.QueryResult{}) default: query := proto.Query{ Sql: bound.Sql, BindVariables: bound.BindVariables, TransactionId: session.TransactionId, SessionId: session.SessionId, } var localReply mproto.QueryResult if err = sq.Execute(context, &query, &localReply); err != nil { if begin_called { sq.Rollback(context, &session, &noOutput) } return err } reply.List = append(reply.List, localReply) } } if begin_called { sq.Rollback(context, &session, &noOutput) panic(NewTabletError(FAIL, "begin called with no commit")) } return nil }
// ExecuteBatch executes a group of queries and returns their results as a list. // ExecuteBatch can be called for an existing transaction, or it can be called with // the AsTransaction flag which will execute all statements inside an independent // transaction. If AsTransaction is true, TransactionId must be 0. func (tsv *TabletServer) ExecuteBatch(ctx context.Context, target *pb.Target, queryList *proto.QueryList, reply *proto.QueryResultList) (err error) { if len(queryList.Queries) == 0 { return NewTabletError(ErrFail, vtrpc.ErrorCode_BAD_INPUT, "Empty query list") } if queryList.AsTransaction && queryList.TransactionId != 0 { return NewTabletError(ErrFail, vtrpc.ErrorCode_BAD_INPUT, "cannot start a new transaction in the scope of an existing one") } allowShutdown := (queryList.TransactionId != 0) if err = tsv.startRequest(target, queryList.SessionId, allowShutdown); err != nil { return err } defer tsv.endRequest() defer handleError(&err, nil, tsv.qe.queryServiceStats) session := proto.Session{ TransactionId: queryList.TransactionId, SessionId: queryList.SessionId, } if queryList.AsTransaction { var txInfo proto.TransactionInfo if err = tsv.Begin(ctx, target, &session, &txInfo); err != nil { return err } session.TransactionId = txInfo.TransactionId // If transaction was not committed by the end, it means // that there was an error, roll it back. defer func() { if session.TransactionId != 0 { tsv.Rollback(ctx, target, &session) } }() } reply.List = make([]mproto.QueryResult, 0, len(queryList.Queries)) for _, bound := range queryList.Queries { query := proto.Query{ Sql: bound.Sql, BindVariables: bound.BindVariables, TransactionId: session.TransactionId, SessionId: session.SessionId, } var localReply mproto.QueryResult if err = tsv.Execute(ctx, target, &query, &localReply); err != nil { return err } reply.List = append(reply.List, localReply) } if queryList.AsTransaction { if err = tsv.Commit(ctx, target, &session); err != nil { session.TransactionId = 0 return err } session.TransactionId = 0 } return nil }
// ExecuteBatch executes a group of queries and returns their results as a list. // ExecuteBatch can be called for an existing transaction, or it can also begin // its own transaction, in which case it's expected to commit it also. func (sq *SqlQuery) ExecuteBatch(ctx context.Context, queryList *proto.QueryList, reply *proto.QueryResultList) (err error) { if len(queryList.Queries) == 0 { return NewTabletError(ErrFail, "Empty query list") } allowShutdown := (queryList.TransactionId != 0) if err = sq.startRequest(queryList.SessionId, false, allowShutdown); err != nil { return err } defer sq.endRequest() defer handleError(&err, nil, sq.qe.queryServiceStats) beginCalled := false session := proto.Session{ TransactionId: queryList.TransactionId, SessionId: queryList.SessionId, } reply.List = make([]mproto.QueryResult, 0, len(queryList.Queries)) for _, bound := range queryList.Queries { trimmed := strings.ToLower(strings.Trim(bound.Sql, " \t\r\n")) switch trimmed { case "begin": if session.TransactionId != 0 { panic(NewTabletError(ErrFail, "Nested transactions disallowed")) } var txInfo proto.TransactionInfo if err = sq.Begin(ctx, &session, &txInfo); err != nil { return err } session.TransactionId = txInfo.TransactionId beginCalled = true reply.List = append(reply.List, mproto.QueryResult{}) case "commit": if !beginCalled { panic(NewTabletError(ErrFail, "Cannot commit without begin")) } if err = sq.Commit(ctx, &session); err != nil { return err } session.TransactionId = 0 beginCalled = false reply.List = append(reply.List, mproto.QueryResult{}) default: query := proto.Query{ Sql: bound.Sql, BindVariables: bound.BindVariables, TransactionId: session.TransactionId, SessionId: session.SessionId, } var localReply mproto.QueryResult if err = sq.Execute(ctx, &query, &localReply); err != nil { if beginCalled { sq.Rollback(ctx, &session) } return err } reply.List = append(reply.List, localReply) } } if beginCalled { sq.Rollback(ctx, &session) panic(NewTabletError(ErrFail, "begin called with no commit")) } return nil }
// AddTabletErrorToQueryResultList will mutate a QueryResultList struct to fill in the Err // field with details from the TabletError. func AddTabletErrorToQueryResultList(err error, reply *proto.QueryResultList) { if err == nil { return } reply.Err = rpcErrFromTabletError(err) }