// BeginExecute starts a transaction and runs an Execute. func (conn *gRPCQueryClient) BeginExecute(ctx context.Context, target *querypb.Target, query string, bindVars map[string]interface{}, options *querypb.ExecuteOptions) (result *sqltypes.Result, transactionID int64, err error) { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { return nil, 0, tabletconn.ConnClosed } q, err := querytypes.BoundQueryToProto3(query, bindVars) if err != nil { return nil, 0, err } if *combo { // If combo is enabled, we combine both calls req := &querypb.BeginExecuteRequest{ Target: target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), Query: q, Options: options, } reply, err := conn.c.BeginExecute(ctx, req) if err != nil { return nil, 0, tabletconn.TabletErrorFromGRPC(err) } if reply.Error != nil { return nil, reply.TransactionId, tabletconn.TabletErrorFromRPCError(reply.Error) } return sqltypes.Proto3ToResult(reply.Result), reply.TransactionId, nil } // Begin part. breq := &querypb.BeginRequest{ Target: target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), } br, err := conn.c.Begin(ctx, breq) if err != nil { return nil, 0, tabletconn.TabletErrorFromGRPC(err) } transactionID = br.TransactionId // Execute part. ereq := &querypb.ExecuteRequest{ Target: target, EffectiveCallerId: breq.EffectiveCallerId, ImmediateCallerId: breq.ImmediateCallerId, Query: q, TransactionId: transactionID, Options: options, } er, err := conn.c.Execute(ctx, ereq) if err != nil { return nil, transactionID, tabletconn.TabletErrorFromGRPC(err) } return sqltypes.Proto3ToResult(er.Result), transactionID, nil }
// ExecuteBatch sends a batch query to VTTablet. func (conn *gRPCQueryClient) ExecuteBatch(ctx context.Context, target *querypb.Target, queries []querytypes.BoundQuery, asTransaction bool, transactionID int64, options *querypb.ExecuteOptions) ([]sqltypes.Result, error) { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { return nil, tabletconn.ConnClosed } req := &querypb.ExecuteBatchRequest{ Target: target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), Queries: make([]*querypb.BoundQuery, len(queries)), AsTransaction: asTransaction, TransactionId: transactionID, Options: options, } for i, q := range queries { qq, err := querytypes.BoundQueryToProto3(q.Sql, q.BindVariables) if err != nil { return nil, err } req.Queries[i] = qq } ebr, err := conn.c.ExecuteBatch(ctx, req) if err != nil { return nil, tabletconn.TabletErrorFromGRPC(err) } return sqltypes.Proto3ToResults(ebr.Results), nil }
// SplitQuery is the stub for TabletServer.SplitQuery RPC // TODO(erez): Remove this method and rename SplitQueryV2 to SplitQuery once // the migration to SplitQuery V2 is done. func (conn *gRPCQueryClient) SplitQuery(ctx context.Context, target *querypb.Target, query querytypes.BoundQuery, splitColumn string, splitCount int64) (queries []querytypes.QuerySplit, err error) { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { err = tabletconn.ConnClosed return } q, err := querytypes.BoundQueryToProto3(query.Sql, query.BindVariables) if err != nil { return nil, tabletconn.TabletErrorFromGRPC(err) } req := &querypb.SplitQueryRequest{ Target: target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), Query: q, SplitColumn: []string{splitColumn}, SplitCount: splitCount, NumRowsPerQueryPart: 0, Algorithm: querypb.SplitQueryRequest_EQUAL_SPLITS, UseSplitQueryV2: false, } sqr, err := conn.c.SplitQuery(ctx, req) if err != nil { return nil, tabletconn.TabletErrorFromGRPC(err) } split, err := querytypes.Proto3ToQuerySplits(sqr.Queries) if err != nil { return nil, tabletconn.TabletErrorFromGRPC(err) } return split, nil }
func (f *FakeQueryService) checkSessionTargetCallerID(ctx context.Context, name string, target *querypb.Target, sessionID int64) { if f.checkTarget { if !reflect.DeepEqual(target, testTarget) { f.t.Errorf("invalid Target for %v: got %#v expected %#v", name, target, testTarget) } } else { if sessionID != testSessionID { f.t.Errorf("invalid sessionID for %v: got %v expected %v", name, sessionID, testSessionID) } } ef := callerid.EffectiveCallerIDFromContext(ctx) if ef == nil { f.t.Errorf("no effective caller id for %v", name) } else { if !reflect.DeepEqual(ef, testCallerID) { f.t.Errorf("invalid effective caller id for %v: got %v expected %v", name, ef, testCallerID) } } im := callerid.ImmediateCallerIDFromContext(ctx) if im == nil { f.t.Errorf("no immediate caller id for %v", name) } else { if !reflect.DeepEqual(im, testVTGateCallerID) { f.t.Errorf("invalid immediate caller id for %v: got %v expected %v", name, im, testVTGateCallerID) } } }
// Execute sends the query to VTTablet. func (conn *gRPCQueryClient) Execute(ctx context.Context, query string, bindVars map[string]interface{}, transactionID int64) (*sqltypes.Result, error) { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { return nil, tabletconn.ConnClosed } q, err := tproto.BoundQueryToProto3(query, bindVars) if err != nil { return nil, err } req := &pb.ExecuteRequest{ Target: conn.target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), Query: q, TransactionId: transactionID, SessionId: conn.sessionID, } er, err := conn.c.Execute(ctx, req) if err != nil { return nil, tabletconn.TabletErrorFromGRPC(err) } return sqltypes.Proto3ToResult(er.Result), nil }
// SplitQuery is the stub for TabletServer.SplitQuery RPC func (conn *gRPCQueryClient) SplitQuery(ctx context.Context, query tproto.BoundQuery, splitColumn string, splitCount int) (queries []tproto.QuerySplit, err error) { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { err = tabletconn.ConnClosed return } q, err := tproto.BoundQueryToProto3(query.Sql, query.BindVariables) if err != nil { return nil, tabletconn.TabletErrorFromGRPC(err) } req := &pb.SplitQueryRequest{ Target: conn.target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), Query: q, SplitColumn: splitColumn, SplitCount: int64(splitCount), SessionId: conn.sessionID, } sqr, err := conn.c.SplitQuery(ctx, req) if err != nil { return nil, tabletconn.TabletErrorFromGRPC(err) } split, err := tproto.Proto3ToQuerySplits(sqr.Queries) if err != nil { return nil, tabletconn.TabletErrorFromGRPC(err) } return split, nil }
// ExecuteBatch sends a batch query to VTTablet. func (conn *gRPCQueryClient) ExecuteBatch(ctx context.Context, queries []tproto.BoundQuery, asTransaction bool, transactionID int64) (*tproto.QueryResultList, error) { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { return nil, tabletconn.ConnClosed } req := &pb.ExecuteBatchRequest{ Target: conn.target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), Queries: make([]*pb.BoundQuery, len(queries)), AsTransaction: asTransaction, TransactionId: transactionID, SessionId: conn.sessionID, } for i, q := range queries { req.Queries[i] = tproto.BoundQueryToProto3(q.Sql, q.BindVariables) } ebr, err := conn.c.ExecuteBatch(ctx, req) if err != nil { return nil, tabletErrorFromGRPC(err) } return tproto.Proto3ToQueryResultList(ebr.Results), nil }
// Begin begins a transaction, and returns the associated transaction id. // Subsequent statements can access the connection through the transaction id. func (axp *TxPool) Begin(ctx context.Context) int64 { poolCtx := ctx if deadline, ok := ctx.Deadline(); ok { var cancel func() poolCtx, cancel = context.WithDeadline(ctx, deadline.Add(-10*time.Millisecond)) defer cancel() } conn, err := axp.pool.Get(poolCtx) if err != nil { switch err { case ErrConnPoolClosed: panic(err) case pools.ErrTimeout: axp.LogActive() panic(NewTabletError(ErrTxPoolFull, vtrpcpb.ErrorCode_RESOURCE_EXHAUSTED, "Transaction pool connection limit exceeded")) } panic(NewTabletErrorSQL(ErrFatal, vtrpcpb.ErrorCode_INTERNAL_ERROR, err)) } if _, err := conn.Exec(ctx, "begin", 1, false); err != nil { conn.Recycle() panic(NewTabletErrorSQL(ErrFail, vtrpcpb.ErrorCode_UNKNOWN_ERROR, err)) } transactionID := axp.lastID.Add(1) axp.activePool.Register( transactionID, newTxConnection( conn, transactionID, axp, callerid.ImmediateCallerIDFromContext(ctx), callerid.EffectiveCallerIDFromContext(ctx), ), ) return transactionID }
func addUserTableQueryStats(queryServiceStats *QueryServiceStats, ctx context.Context, tableName string, queryType string, duration int64) { username := callerid.GetPrincipal(callerid.EffectiveCallerIDFromContext(ctx)) if username == "" { username = callerid.GetUsername(callerid.ImmediateCallerIDFromContext(ctx)) } queryServiceStats.UserTableQueryCount.Add([]string{tableName, username, queryType}, 1) queryServiceStats.UserTableQueryTimesNs.Add([]string{tableName, username, queryType}, int64(duration)) }
func getImmediateCallerID(ctx context.Context) *tproto.VTGateCallerID { if im := callerid.ImmediateCallerIDFromContext(ctx); im != nil { return &tproto.VTGateCallerID{ Username: im.Username, } } return nil }
// StreamExecute starts a streaming query to VTTablet. func (conn *gRPCQueryClient) StreamExecute(ctx context.Context, query string, bindVars map[string]interface{}, transactionID int64) (<-chan *sqltypes.Result, tabletconn.ErrFunc, error) { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { return nil, nil, tabletconn.ConnClosed } q, err := querytypes.BoundQueryToProto3(query, bindVars) if err != nil { return nil, nil, err } req := &querypb.StreamExecuteRequest{ Target: conn.target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), Query: q, SessionId: conn.sessionID, } stream, err := conn.c.StreamExecute(ctx, req) if err != nil { return nil, nil, tabletconn.TabletErrorFromGRPC(err) } sr := make(chan *sqltypes.Result, 10) var finalError error go func() { var fields []*querypb.Field for { ser, err := stream.Recv() if err != nil { if err != io.EOF { finalError = tabletconn.TabletErrorFromGRPC(err) } close(sr) return } if fields == nil { fields = ser.Result.Fields } sr <- sqltypes.CustomProto3ToResult(fields, ser.Result) } }() return sr, func() error { return finalError }, nil }
// Begin starts a transaction. func (conn *gRPCQueryClient) Begin(ctx context.Context, target *querypb.Target) (transactionID int64, err error) { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { return 0, tabletconn.ConnClosed } req := &querypb.BeginRequest{ Target: target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), } br, err := conn.c.Begin(ctx, req) if err != nil { return 0, tabletconn.TabletErrorFromGRPC(err) } return br.TransactionId, nil }
// ReadTransaction returns the metadata for the sepcified dtid. func (conn *gRPCQueryClient) ReadTransaction(ctx context.Context, target *querypb.Target, dtid string) (*querypb.TransactionMetadata, error) { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { return nil, tabletconn.ConnClosed } req := &querypb.ReadTransactionRequest{ Target: target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), Dtid: dtid, } response, err := conn.c.ReadTransaction(ctx, req) if err != nil { return nil, tabletconn.TabletErrorFromGRPC(err) } return response.Metadata, nil }
// CommitPrepared commits the prepared transaction. func (conn *gRPCQueryClient) CommitPrepared(ctx context.Context, target *querypb.Target, dtid string) error { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { return tabletconn.ConnClosed } req := &querypb.CommitPreparedRequest{ Target: target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), Dtid: dtid, } _, err := conn.c.CommitPrepared(ctx, req) if err != nil { return tabletconn.TabletErrorFromGRPC(err) } return nil }
// Rollback rolls back the ongoing transaction. func (conn *gRPCQueryClient) Rollback(ctx context.Context, target *querypb.Target, transactionID int64) error { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { return tabletconn.ConnClosed } req := &querypb.RollbackRequest{ Target: target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), TransactionId: transactionID, } _, err := conn.c.Rollback(ctx, req) if err != nil { return tabletconn.TabletErrorFromGRPC(err) } return nil }
// CreateTransaction creates the metadata for a 2PC transaction. func (conn *gRPCQueryClient) CreateTransaction(ctx context.Context, target *querypb.Target, dtid string, participants []*querypb.Target) error { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { return tabletconn.ConnClosed } req := &querypb.CreateTransactionRequest{ Target: target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), Dtid: dtid, Participants: participants, } _, err := conn.c.CreateTransaction(ctx, req) if err != nil { return tabletconn.TabletErrorFromGRPC(err) } return nil }
// UpdateStream starts a streaming query to VTTablet. func (conn *gRPCQueryClient) UpdateStream(ctx context.Context, target *querypb.Target, position string, timestamp int64) (tabletconn.StreamEventReader, error) { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { return nil, tabletconn.ConnClosed } req := &querypb.UpdateStreamRequest{ Target: target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), Position: position, Timestamp: timestamp, } stream, err := conn.c.UpdateStream(ctx, req) if err != nil { return nil, tabletconn.TabletErrorFromGRPC(err) } return &updateStreamAdapter{stream: stream}, err }
// StreamExecute starts a streaming query to VTTablet. func (conn *gRPCQueryClient) StreamExecute(ctx context.Context, query string, bindVars map[string]interface{}) (sqltypes.ResultStream, error) { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { return nil, tabletconn.ConnClosed } q, err := querytypes.BoundQueryToProto3(query, bindVars) if err != nil { return nil, err } req := &querypb.StreamExecuteRequest{ Target: conn.target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), Query: q, } stream, err := conn.c.StreamExecute(ctx, req) if err != nil { return nil, tabletconn.TabletErrorFromGRPC(err) } return &streamExecuteAdapter{stream: stream}, err }
// RunTests performs the necessary testsuite for CallerID operations func RunTests(t *testing.T, im *querypb.VTGateCallerID, ef *vtrpcpb.CallerID, newContext func(context.Context, *vtrpcpb.CallerID, *querypb.VTGateCallerID) context.Context) { ctx := context.TODO() ctxim := callerid.ImmediateCallerIDFromContext(ctx) // For Contexts without immediate CallerID, ImmediateCallerIDFromContext should fail if ctxim != nil { t.Errorf("Expect nil from ImmediateCallerIDFromContext, but got %v", ctxim) } // For Contexts without effective CallerID, EffectiveCallerIDFromContext should fail ctxef := callerid.EffectiveCallerIDFromContext(ctx) if ctxef != nil { t.Errorf("Expect nil from EffectiveCallerIDFromContext, but got %v", ctxef) } ctx = newContext(ctx, nil, nil) ctxim = callerid.ImmediateCallerIDFromContext(ctx) // For Contexts with nil immediate CallerID, ImmediateCallerIDFromContext should fail if ctxim != nil { t.Errorf("Expect nil from ImmediateCallerIDFromContext, but got %v", ctxim) } // For Contexts with nil effective CallerID, EffectiveCallerIDFromContext should fail ctxef = callerid.EffectiveCallerIDFromContext(ctx) if ctxef != nil { t.Errorf("Expect nil from EffectiveCallerIDFromContext, but got %v", ctxef) } // Test GetXxx on nil receivers, should get all empty strings if u := callerid.GetUsername(ctxim); u != "" { t.Errorf("Expect empty string from GetUsername(nil), but got %v", u) } if p := callerid.GetPrincipal(ctxef); p != "" { t.Errorf("Expect empty string from GetPrincipal(nil), but got %v", p) } if c := callerid.GetComponent(ctxef); c != "" { t.Errorf("Expect empty string from GetComponent(nil), but got %v", c) } if s := callerid.GetSubcomponent(ctxef); s != "" { t.Errorf("Expect empty string from GetSubcomponent(nil), but got %v", s) } ctx = newContext(ctx, ef, im) ctxim = callerid.ImmediateCallerIDFromContext(ctx) // retrieved immediate CallerID should be equal to the one we put into Context if !reflect.DeepEqual(ctxim, im) { t.Errorf("Expect %v from ImmediateCallerIDFromContext, but got %v", im, ctxim) } if u := callerid.GetUsername(im); u != FakeUsername { t.Errorf("Expect %v from GetUsername(im), but got %v", FakeUsername, u) } ctxef = callerid.EffectiveCallerIDFromContext(ctx) // retrieved effective CallerID should be equal to the one we put into Context if !reflect.DeepEqual(ctxef, ef) { t.Errorf("Expect %v from EffectiveCallerIDFromContext, but got %v", ef, ctxef) } if p := callerid.GetPrincipal(ef); p != FakePrincipal { t.Errorf("Expect %v from GetPrincipal(ef), but got %v", FakePrincipal, p) } if c := callerid.GetComponent(ef); c != FakeComponent { t.Errorf("Expect %v from GetComponent(ef), but got %v", FakeComponent, c) } if s := callerid.GetSubcomponent(ef); s != FakeSubcomponent { t.Errorf("Expect %v from GetSubcomponent(ef), but got %v", FakeSubcomponent, s) } }
// checkPermissions func (qre *QueryExecutor) checkPermissions() error { // Skip permissions check if we have a background context. if qre.ctx == context.Background() { return nil } // Blacklist remoteAddr := "" username := "" ci, ok := callinfo.FromContext(qre.ctx) if ok { remoteAddr = ci.RemoteAddr() username = ci.Username() } action, desc := qre.plan.Rules.getAction(remoteAddr, username, qre.bindVars) switch action { case QR_FAIL: return NewTabletError(ErrFail, vtrpc.ErrorCode_BAD_INPUT, "Query disallowed due to rule: %s", desc) case QR_FAIL_RETRY: return NewTabletError(ErrRetry, vtrpc.ErrorCode_QUERY_NOT_SERVED, "Query disallowed due to rule: %s", desc) } callerID := callerid.ImmediateCallerIDFromContext(qre.ctx) if callerID == nil { if qre.qe.strictTableAcl { return NewTabletError(ErrFail, vtrpc.ErrorCode_UNAUTHENTICATED, "missing caller id") } return nil } // a superuser that exempts from table ACL checking. if qre.qe.exemptACL == callerID.Username { qre.qe.tableaclExemptCount.Add(1) return nil } // empty table name, do not need a table ACL check. if qre.plan.TableName == "" { return nil } if qre.plan.Authorized == nil { return NewTabletError(ErrFail, vtrpc.ErrorCode_PERMISSION_DENIED, "table acl error: nil acl") } tableACLStatsKey := []string{ qre.plan.TableName, qre.plan.Authorized.GroupName, qre.plan.PlanId.String(), callerID.Username, } // perform table ACL check if it is enabled. if !qre.plan.Authorized.IsMember(callerID.Username) { if qre.qe.enableTableAclDryRun { qre.qe.tableaclPseudoDenied.Add(tableACLStatsKey, 1) return nil } // raise error if in strictTableAcl mode, else just log an error. if qre.qe.strictTableAcl { errStr := fmt.Sprintf("table acl error: %q cannot run %v on table %q", callerID.Username, qre.plan.PlanId, qre.plan.TableName) qre.qe.tableaclDenied.Add(tableACLStatsKey, 1) qre.qe.accessCheckerLogger.Errorf("%s", errStr) return NewTabletError(ErrFail, vtrpc.ErrorCode_PERMISSION_DENIED, "%s", errStr) } return nil } qre.qe.tableaclAllowed.Add(tableACLStatsKey, 1) return nil }
// BeginExecuteBatch starts a transaction and runs an ExecuteBatch. func (conn *gRPCQueryClient) BeginExecuteBatch(ctx context.Context, target *querypb.Target, queries []querytypes.BoundQuery, asTransaction bool, options *querypb.ExecuteOptions) (results []sqltypes.Result, transactionID int64, err error) { conn.mu.RLock() defer conn.mu.RUnlock() if conn.cc == nil { return nil, 0, tabletconn.ConnClosed } if *combo { // If combo is enabled, we combine both calls req := &querypb.BeginExecuteBatchRequest{ Target: target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), Queries: make([]*querypb.BoundQuery, len(queries)), AsTransaction: asTransaction, Options: options, } for i, q := range queries { qq, err := querytypes.BoundQueryToProto3(q.Sql, q.BindVariables) if err != nil { return nil, transactionID, err } req.Queries[i] = qq } reply, err := conn.c.BeginExecuteBatch(ctx, req) if err != nil { return nil, 0, tabletconn.TabletErrorFromGRPC(err) } if reply.Error != nil { return nil, reply.TransactionId, tabletconn.TabletErrorFromRPCError(reply.Error) } return sqltypes.Proto3ToResults(reply.Results), reply.TransactionId, nil } breq := &querypb.BeginRequest{ Target: target, EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx), ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx), } br, err := conn.c.Begin(ctx, breq) if err != nil { return nil, 0, tabletconn.TabletErrorFromGRPC(err) } transactionID = br.TransactionId ereq := &querypb.ExecuteBatchRequest{ Target: target, EffectiveCallerId: breq.EffectiveCallerId, ImmediateCallerId: breq.ImmediateCallerId, Queries: make([]*querypb.BoundQuery, len(queries)), AsTransaction: asTransaction, TransactionId: transactionID, Options: options, } for i, q := range queries { qq, err := querytypes.BoundQueryToProto3(q.Sql, q.BindVariables) if err != nil { return nil, transactionID, err } ereq.Queries[i] = qq } ebr, err := conn.c.ExecuteBatch(ctx, ereq) if err != nil { return nil, transactionID, tabletconn.TabletErrorFromGRPC(err) } return sqltypes.Proto3ToResults(ebr.Results), transactionID, nil }
// ImmediateCaller returns the immediate caller stored in LogStats.ctx func (stats *LogStats) ImmediateCaller() string { return callerid.GetUsername(callerid.ImmediateCallerIDFromContext(stats.ctx)) }