// StreamExecute executes the query and streams its result. // The first QueryResult will have Fields set (and Rows nil) // The subsequent QueryResult will have Rows set (and Fields nil) func (qe *QueryEngine) StreamExecute(logStats *SQLQueryStats, query *proto.Query, sendReply func(*mproto.QueryResult) error) { if query.BindVariables == nil { // will help us avoid repeated nil checks query.BindVariables = make(map[string]interface{}) } logStats.BindVariables = query.BindVariables // cheap hack: strip trailing comment into a special bind var stripTrailing(query) plan := qe.schemaInfo.GetStreamPlan(query.Sql) logStats.PlanType = "SELECT_STREAM" logStats.OriginalSql = query.Sql defer queryStats.Record("SELECT_STREAM", time.Now()) authorized := tableacl.Authorized(plan.TableName, plan.PlanId.MinRole()) qe.checkTableAcl(plan.TableName, plan.PlanId, authorized, logStats.context.GetUsername()) // does the real work: first get a connection waitingForConnectionStart := time.Now() conn := getOrPanic(qe.streamConnPool) logStats.WaitingForConnection += time.Now().Sub(waitingForConnectionStart) defer conn.Recycle() qd := NewQueryDetail(query, logStats.context, conn.Id()) qe.streamQList.Add(qd) defer qe.streamQList.Remove(qd) // then let's stream! Wrap callback function to return an // error on query termination to stop further streaming qe.fullStreamFetch(logStats, conn, plan.FullQuery, query.BindVariables, nil, nil, sendReply) }
func checkAccess(configData []byte, tableName string, role tableacl.Role, t *testing.T, want bool) { checkLoad(configData, true, t) got := tableacl.Authorized(tableName, role).IsMember(currentUser()) if want != got { t.Errorf("got %v, want %v", got, want) } }
// GetPlan returns the ExecPlan that for the query. Plans are cached in a cache.LRUCache. func (si *SchemaInfo) GetPlan(ctx context.Context, logStats *LogStats, sql string) *ExecPlan { span := trace.NewSpanFromContext(ctx) span.StartLocal("SchemaInfo.GetPlan") defer span.Finish() // Fastpath if plan already exists. if plan := si.getQuery(sql); plan != nil { return plan } // TODO(sougou): It's not correct to hold this lock here because the code // below runs queries against MySQL. But if we don't hold the lock, there // are other race conditions where identical queries will end up building // plans and compete with populating the query cache. In other words, we // need a more elaborate scheme that blocks less, but still prevents these // race conditions. si.mu.Lock() defer si.mu.Unlock() // Recheck. A plan might have been built by someone else. if plan := si.getQuery(sql); plan != nil { return plan } var tableInfo *TableInfo GetTable := func(tableName string) (table *schema.Table, ok bool) { tableInfo, ok = si.tables[tableName] if !ok { return nil, false } return tableInfo.Table, true } splan, err := planbuilder.GetExecPlan(sql, GetTable) if err != nil { panic(PrefixTabletError(vtrpcpb.ErrorCode_UNKNOWN_ERROR, err, "")) } plan := &ExecPlan{ExecPlan: splan, TableInfo: tableInfo} plan.Rules = si.queryRuleSources.filterByPlan(sql, plan.PlanID, plan.TableName) plan.Authorized = tableacl.Authorized(plan.TableName, plan.PlanID.MinRole()) if plan.PlanID.IsSelect() { if plan.FieldQuery == nil { log.Warningf("Cannot cache field info: %s", sql) } else { conn := getOrPanic(ctx, si.connPool) defer conn.Recycle() sql := plan.FieldQuery.Query start := time.Now() r, err := conn.Exec(ctx, sql, 1, true) logStats.AddRewrittenSQL(sql, start) if err != nil { panic(PrefixTabletError(vtrpcpb.ErrorCode_UNKNOWN_ERROR, err, "Error fetching fields: ")) } plan.Fields = r.Fields } } else if plan.PlanID == planbuilder.PlanDDL || plan.PlanID == planbuilder.PlanSet { return plan } si.queries.Set(sql, plan) return plan }
func checkAccess(config *tableaclpb.Config, tableName string, role tableacl.Role, want bool) error { if err := checkLoad(config, true); err != nil { return err } got := tableacl.Authorized(tableName, role).IsMember(currentUser()) if want != got { return fmt.Errorf("got %v, want %v", got, want) } return nil }
func (si *SchemaInfo) GetPlan(logStats *SQLQueryStats, sql string) (plan *ExecPlan) { // Fastpath if plan already exists. if plan := si.getQuery(sql); plan != nil { return plan } si.mu.Lock() defer si.mu.Unlock() // Recheck. A plan might have been built by someone else. if plan := si.getQuery(sql); plan != nil { return plan } var tableInfo *TableInfo GetTable := func(tableName string) (table *schema.Table, ok bool) { tableInfo, ok = si.tables[tableName] if !ok { return nil, false } return tableInfo.Table, true } splan, err := planbuilder.GetExecPlan(sql, GetTable) if err != nil { panic(NewTabletError(FAIL, "%s", err)) } plan = &ExecPlan{ExecPlan: splan, TableInfo: tableInfo} plan.Rules = si.rules.filterByPlan(sql, plan.PlanId, plan.TableName) plan.Authorized = tableacl.Authorized(plan.TableName, plan.PlanId.MinRole()) if plan.PlanId.IsSelect() { if plan.FieldQuery == nil { log.Warningf("Cannot cache field info: %s", sql) } else { conn := getOrPanic(si.connPool) defer conn.Recycle() sql := plan.FieldQuery.Query r, err := conn.ExecuteFetch(sql, 1, true) logStats.QuerySources |= QUERY_SOURCE_MYSQL logStats.NumberOfQueries += 1 logStats.AddRewrittenSql(sql) if err != nil { panic(NewTabletError(FAIL, "Error fetching fields: %v", err)) } plan.Fields = r.Fields } } else if plan.PlanId == planbuilder.PLAN_DDL || plan.PlanId == planbuilder.PLAN_SET { return plan } si.queries.Set(sql, plan) return plan }
// GetPlan returns the ExecPlan that for the query. Plans are cached in a cache.LRUCache. func (si *SchemaInfo) GetPlan(ctx context.Context, logStats *SQLQueryStats, sql string) *ExecPlan { // Fastpath if plan already exists. if plan := si.getQuery(sql); plan != nil { return plan } si.mu.Lock() defer si.mu.Unlock() // Recheck. A plan might have been built by someone else. if plan := si.getQuery(sql); plan != nil { return plan } var tableInfo *TableInfo GetTable := func(tableName string) (table *schema.Table, ok bool) { tableInfo, ok = si.tables[tableName] if !ok { return nil, false } return tableInfo.Table, true } splan, err := planbuilder.GetExecPlan(sql, GetTable) if err != nil { panic(PrefixTabletError(ErrFail, vtrpc.ErrorCode_UNKNOWN_ERROR, err, "")) } plan := &ExecPlan{ExecPlan: splan, TableInfo: tableInfo} plan.Rules = QueryRuleSources.filterByPlan(sql, plan.PlanId, plan.TableName) plan.Authorized = tableacl.Authorized(plan.TableName, plan.PlanId.MinRole()) if plan.PlanId.IsSelect() { if plan.FieldQuery == nil { log.Warningf("Cannot cache field info: %s", sql) } else { conn := getOrPanic(ctx, si.connPool) defer conn.Recycle() sql := plan.FieldQuery.Query start := time.Now() r, err := conn.Exec(ctx, sql, 1, true) logStats.AddRewrittenSql(sql, start) if err != nil { panic(PrefixTabletError(ErrFail, vtrpc.ErrorCode_UNKNOWN_ERROR, err, "Error fetching fields: ")) } plan.Fields = r.Fields } } else if plan.PlanId == planbuilder.PLAN_DDL || plan.PlanId == planbuilder.PLAN_SET { return plan } si.queries.Set(sql, plan) return plan }
// GetStreamPlan is similar to GetPlan, but doesn't use the cache // and doesn't enforce a limit. It just returns the parsed query. func (si *SchemaInfo) GetStreamPlan(sql string) *ExecPlan { var tableInfo *TableInfo GetTable := func(tableName string) (table *schema.Table, ok bool) { si.mu.Lock() defer si.mu.Unlock() tableInfo, ok = si.tables[tableName] if !ok { return nil, false } return tableInfo.Table, true } splan, err := planbuilder.GetStreamExecPlan(sql, GetTable) if err != nil { panic(PrefixTabletError(ErrFail, vtrpc.ErrorCode_UNKNOWN_ERROR, err, "")) } plan := &ExecPlan{ExecPlan: splan, TableInfo: tableInfo} plan.Rules = si.queryRuleSources.filterByPlan(sql, plan.PlanId, plan.TableName) plan.Authorized = tableacl.Authorized(plan.TableName, plan.PlanId.MinRole()) return plan }
// GetStreamPlan is similar to GetPlan, but doesn't use the cache // and doesn't enforce a limit. It just returns the parsed query. func (si *SchemaInfo) GetStreamPlan(sql string) *ExecPlan { var tableInfo *TableInfo GetTable := func(tableName string) (table *schema.Table, ok bool) { si.mu.Lock() defer si.mu.Unlock() tableInfo, ok = si.tables[tableName] if !ok { return nil, false } return tableInfo.Table, true } splan, err := planbuilder.GetStreamExecPlan(sql, GetTable) if err != nil { panic(NewTabletError(FAIL, "%s", err)) } plan := &ExecPlan{ExecPlan: splan, TableInfo: tableInfo} plan.Rules = si.rules.filterByPlan(sql, plan.PlanId, plan.TableName) plan.Authorized = tableacl.Authorized(plan.TableName, plan.PlanId.MinRole()) return plan }