// StreamExecute executes the query and streams the result. // The first QueryResult will have Fields set (and Rows nil). // The subsequent QueryResult will have Rows set (and Fields nil). func (sq *SqlQuery) StreamExecute(context context.Context, query *proto.Query, sendReply func(*mproto.QueryResult) error) (err error) { // check cases we don't handle yet if query.TransactionId != 0 { return NewTabletError(FAIL, "Transactions not supported with streaming") } logStats := newSqlQueryStats("StreamExecute", context) if err = sq.startRequest(query.SessionId, false); err != nil { return err } defer sq.endRequest() defer handleExecError(query, &err, logStats) // TODO(sougou): Change usage such that we don't have to do this. if query.BindVariables == nil { query.BindVariables = make(map[string]interface{}) } stripTrailing(query) qre := &QueryExecutor{ query: query.Sql, bindVars: query.BindVariables, transactionID: query.TransactionId, plan: sq.qe.schemaInfo.GetStreamPlan(query.Sql, sq.qe.queryRuleInfo), RequestContext: RequestContext{ ctx: context, logStats: logStats, qe: sq.qe, deadline: NewDeadline(sq.qe.queryTimeout.Get()), }, } qre.Stream(sendReply) return nil }
// Execute executes the query and returns the result as response. func (sq *SqlQuery) Execute(context context.Context, query *proto.Query, reply *mproto.QueryResult) (err error) { logStats := newSqlQueryStats("Execute", context) allowShutdown := (query.TransactionId != 0) if err = sq.startRequest(query.SessionId, allowShutdown); err != nil { return err } defer sq.endRequest() defer handleExecError(query, &err, logStats) // TODO(sougou): Change usage such that we don't have to do this. if query.BindVariables == nil { query.BindVariables = make(map[string]interface{}) } stripTrailing(query) qre := &QueryExecutor{ query: query.Sql, bindVars: query.BindVariables, transactionID: query.TransactionId, plan: sq.qe.schemaInfo.GetPlan(logStats, query.Sql, sq.qe.queryRuleInfo), RequestContext: RequestContext{ ctx: context, logStats: logStats, qe: sq.qe, deadline: NewDeadline(sq.qe.queryTimeout.Get()), }, } *reply = *qre.Execute() return nil }
// stripTrailing strips out trailing comments if any and puts them in a bind variable. // This code is a hack. Will need cleaning if it evolves beyond this. func stripTrailing(query *proto.Query) { tracker := matchtracker{ query: query.Sql, index: len(query.Sql), } pos := tracker.matchComments() if pos >= 0 { query.Sql = tracker.query[:pos] query.BindVariables[TRAILING_COMMENT] = tracker.query[pos:] } }
func TestComments(t *testing.T) { for _, testCase := range testCases { query := proto.Query{ Sql: testCase.input, BindVariables: make(map[string]interface{}), } stripTrailing(&query) want := proto.Query{ Sql: testCase.outSql, } want.BindVariables = make(map[string]interface{}) if testCase.outVar != "" { want.BindVariables[TRAILING_COMMENT] = testCase.outVar } if !reflect.DeepEqual(query, want) { t.Errorf("test input: '%s', got\n%+v, want\n%+v", testCase.input, query, want) } } }