// Execute executes a non-streaming query by routing based on the values in the query. func (vtg *VTGate) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType pb.TabletType, session *proto.Session, notInTransaction bool, reply *proto.QueryResult) error { startTime := time.Now() statsKey := []string{"Execute", "Any", strings.ToLower(tabletType.String())} defer vtg.timings.Record(statsKey, startTime) x := vtg.inFlight.Add(1) defer vtg.inFlight.Add(-1) if 0 < vtg.maxInFlight && vtg.maxInFlight < x { return errTooManyInFlight } qr, err := vtg.router.Execute(ctx, sql, bindVariables, tabletType, session, notInTransaction) if err == nil { reply.Result = qr vtg.rowsReturned.Add(statsKey, int64(len(qr.Rows))) } else { query := map[string]interface{}{ "Sql": sql, "BindVariables": bindVariables, "TabletType": strings.ToLower(tabletType.String()), "Session": session, "NotInTransaction": notInTransaction, } reply.Error = handleExecuteError(err, statsKey, query, vtg.logExecute).Error() reply.Err = vterrors.RPCErrFromVtError(err) } reply.Session = session return nil }
// ExecuteEntityIds excutes a non-streaming query based on given KeyspaceId map. func (vtg *VTGate) ExecuteEntityIds(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, entityColumnName string, entityKeyspaceIDs []*pbg.ExecuteEntityIdsRequest_EntityId, tabletType pb.TabletType, session *proto.Session, notInTransaction bool, reply *proto.QueryResult) error { startTime := time.Now() statsKey := []string{"ExecuteEntityIds", keyspace, strings.ToLower(tabletType.String())} defer vtg.timings.Record(statsKey, startTime) x := vtg.inFlight.Add(1) defer vtg.inFlight.Add(-1) if 0 < vtg.maxInFlight && vtg.maxInFlight < x { return errTooManyInFlight } sql = sqlannotation.AddFilteredReplicationUnfriendlyIfDML(sql) qr, err := vtg.resolver.ExecuteEntityIds(ctx, sql, bindVariables, keyspace, entityColumnName, entityKeyspaceIDs, tabletType, session, notInTransaction) if err == nil { reply.Result = qr vtg.rowsReturned.Add(statsKey, int64(len(qr.Rows))) } else { query := map[string]interface{}{ "Sql": sql, "BindVariables": bindVariables, "Keyspace": keyspace, "EntityColumnName": entityColumnName, "EntityKeyspaceIDs": entityKeyspaceIDs, "TabletType": strings.ToLower(tabletType.String()), "Session": session, "NotInTransaction": notInTransaction, } reply.Error = handleExecuteError(err, statsKey, query, vtg.logExecuteEntityIds).Error() reply.Err = vterrors.RPCErrFromVtError(err) } reply.Session = session return nil }
// requestToPartialError attempts to return a partial error for a given request. // This partial error should only be returned by Execute* calls. func requestToPartialError(request string) *mproto.RPCError { if !strings.HasPrefix(request, PartialErrorPrefix) { return nil } err := trimmedRequestToError(strings.TrimPrefix(request, PartialErrorPrefix)) return vterrors.RPCErrFromVtError(err) }
// StreamExecuteKeyRanges2 is the RPC version of // vtgateservice.VTGateService method func (vtg *VTGate) StreamExecuteKeyRanges2(ctx context.Context, request *gorpcvtgatecommon.KeyRangeQuery, sendReply func(interface{}) error) (err error) { defer vtg.server.HandlePanic(&err) ctx = callerid.NewContext(ctx, callerid.GoRPCEffectiveCallerID(request.CallerID), callerid.NewImmediateCallerID("gorpc client")) vtgErr := vtg.server.StreamExecuteKeyRanges(ctx, request.Sql, request.BindVariables, request.Keyspace, request.KeyRanges, request.TabletType, func(value *sqltypes.Result) error { return sendReply(&gorpcvtgatecommon.QueryResult{ Result: value, }) }) if vtgErr == nil { return nil } // If there was an app error, send a QueryResult back with it. return sendReply(&gorpcvtgatecommon.QueryResult{ Err: vterrors.RPCErrFromVtError(vtgErr), }) }
// Rollback2 is the RPC version of vtgateservice.VTGateService method func (vtg *VTGate) Rollback2(ctx context.Context, request *gorpcvtgatecommon.RollbackRequest, reply *gorpcvtgatecommon.RollbackResponse) (err error) { defer vtg.server.HandlePanic(&err) ctx, cancel := context.WithDeadline(ctx, time.Now().Add(*rpcTimeout)) defer cancel() ctx = callerid.NewContext(ctx, callerid.GoRPCEffectiveCallerID(request.CallerID), callerid.NewImmediateCallerID("gorpc client")) sessionFromRPC(request.Session) vtgErr := vtg.server.Rollback(ctx, request.Session) reply.Err = vterrors.RPCErrFromVtError(vtgErr) return nil }
// Begin2 is the RPC version of vtgateservice.VTGateService method func (vtg *VTGate) Begin2(ctx context.Context, request *gorpcvtgatecommon.BeginRequest, reply *gorpcvtgatecommon.BeginResponse) (err error) { defer vtg.server.HandlePanic(&err) ctx, cancel := context.WithDeadline(ctx, time.Now().Add(*rpcTimeout)) defer cancel() ctx = callerid.NewContext(ctx, callerid.GoRPCEffectiveCallerID(request.CallerID), callerid.NewImmediateCallerID("gorpc client")) // Don't pass in a nil pointer session, vtgErr := vtg.server.Begin(ctx) reply.Session = sessionToRPC(session) reply.Err = vterrors.RPCErrFromVtError(vtgErr) return nil }
// requestToPartialError fills reply for a partial error if requested. // It returns true if a partial error was requested, false otherwise. // This partial error should only be returned by Execute* calls. func requestToPartialError(request string, session *vtgatepb.Session, reply *proto.QueryResult) bool { if !strings.HasPrefix(request, PartialErrorPrefix) { return false } request = strings.TrimPrefix(request, PartialErrorPrefix) parts := strings.Split(request, "/") rpcErr := vterrors.RPCErrFromVtError(trimmedRequestToError(parts[0])) reply.Err = rpcErr reply.Session = session if len(parts) > 1 && parts[1] == "close transaction" { reply.Session.InTransaction = false } return true }
// SplitQuery is the RPC version of vtgateservice.VTGateService method func (vtg *VTGate) SplitQuery(ctx context.Context, request *gorpcvtgatecommon.SplitQueryRequest, reply *gorpcvtgatecommon.SplitQueryResult) (err error) { defer vtg.server.HandlePanic(&err) ctx, cancel := context.WithDeadline(ctx, time.Now().Add(*rpcTimeout)) defer cancel() ctx = callerid.NewContext(ctx, callerid.GoRPCEffectiveCallerID(request.CallerID), callerid.NewImmediateCallerID("gorpc client")) var vtgErr error reply.Splits, vtgErr = vtg.server.SplitQuery(ctx, request.Keyspace, request.Query.Sql, request.Query.BindVariables, request.SplitColumn, request.SplitCount) reply.Err = vterrors.RPCErrFromVtError(vtgErr) return nil }
// Execute is the RPC version of vtgateservice.VTGateService method func (vtg *VTGate) Execute(ctx context.Context, request *gorpcvtgatecommon.Query, reply *gorpcvtgatecommon.QueryResult) (err error) { defer vtg.server.HandlePanic(&err) ctx, cancel := context.WithDeadline(ctx, time.Now().Add(*rpcTimeout)) defer cancel() ctx = callerid.NewContext(ctx, callerid.GoRPCEffectiveCallerID(request.CallerID), callerid.NewImmediateCallerID("gorpc client")) sessionFromRPC(request.Session) var vtgErr error reply.Result, vtgErr = vtg.server.Execute(ctx, request.Sql, request.BindVariables, request.TabletType, request.Session, request.NotInTransaction) reply.Session = sessionToRPC(request.Session) reply.Err = vterrors.RPCErrFromVtError(vtgErr) return nil }
// ExecuteBatchShards executes a group of queries on the specified shards. func (vtg *VTGate) ExecuteBatchShards(ctx context.Context, queries []proto.BoundShardQuery, tabletType pb.TabletType, asTransaction bool, session *proto.Session, reply *proto.QueryResultList) error { startTime := time.Now() statsKey := []string{"ExecuteBatchShards", "", ""} defer vtg.timings.Record(statsKey, startTime) x := vtg.inFlight.Add(1) defer vtg.inFlight.Add(-1) if 0 < vtg.maxInFlight && vtg.maxInFlight < x { return errTooManyInFlight } annotateBoundShardQueriesAsUnfriendly(queries) qrs, err := vtg.resolver.ExecuteBatch( ctx, tabletType, asTransaction, session, func() (*scatterBatchRequest, error) { return boundShardQueriesToScatterBatchRequest(queries), nil }) if err == nil { reply.List = qrs.List var rowCount int64 for _, qr := range qrs.List { rowCount += int64(len(qr.Rows)) } vtg.rowsReturned.Add(statsKey, rowCount) } else { query := map[string]interface{}{ "Queries": queries, "TabletType": strings.ToLower(tabletType.String()), "AsTransaction": asTransaction, "Session": session, } reply.Error = handleExecuteError(err, statsKey, query, vtg.logExecuteBatchShards).Error() reply.Err = vterrors.RPCErrFromVtError(err) } reply.Session = session return nil }
// ExecuteBatchKeyspaceIds is the RPC version of // vtgateservice.VTGateService method func (vtg *VTGate) ExecuteBatchKeyspaceIds(ctx context.Context, request *gorpcvtgatecommon.KeyspaceIdBatchQuery, reply *gorpcvtgatecommon.QueryResultList) (err error) { defer vtg.server.HandlePanic(&err) ctx, cancel := context.WithDeadline(ctx, time.Now().Add(*rpcTimeout)) defer cancel() ctx = callerid.NewContext(ctx, callerid.GoRPCEffectiveCallerID(request.CallerID), callerid.NewImmediateCallerID("gorpc client")) sessionFromRPC(request.Session) qs, err := gorpcvtgatecommon.BoundKeyspaceIdQueriesToProto(request.Queries) if err != nil { return err } var vtgErr error reply.List, vtgErr = vtg.server.ExecuteBatchKeyspaceIds(ctx, qs, request.TabletType, request.AsTransaction, request.Session) reply.Session = sessionToRPC(request.Session) reply.Err = vterrors.RPCErrFromVtError(vtgErr) return nil }
// AddVtGateError will update a mproto.RPCError with details from a VTGate error. func AddVtGateError(err error, replyErr **mproto.RPCError) { if err == nil { return } *replyErr = vterrors.RPCErrFromVtError(err) }