зеркало из https://github.com/github/vitess-gh.git
Merge branch 'master' into k8s_end2end
This commit is contained in:
Коммит
8b5d02e69d
|
@ -6,6 +6,21 @@
|
|||
"delete from nouser"
|
||||
"table nouser not found"
|
||||
|
||||
# explicit keyspace reference
|
||||
"update main.m1 set val = 1"
|
||||
{
|
||||
"Original": "update main.m1 set val = 1",
|
||||
"Instructions": {
|
||||
"Opcode": "UpdateUnsharded",
|
||||
"Keyspace": {
|
||||
"Name": "main",
|
||||
"Sharded": false
|
||||
},
|
||||
"Query": "update m1 set val = 1",
|
||||
"Table": "m1"
|
||||
}
|
||||
}
|
||||
|
||||
# update unsharded
|
||||
"update main1 set val = 1"
|
||||
{
|
||||
|
|
|
@ -790,6 +790,25 @@
|
|||
}
|
||||
}
|
||||
|
||||
# implicit table reference for unsharded keyspace
|
||||
"select main.foo.col from main.foo"
|
||||
{
|
||||
"Original": "select main.foo.col from main.foo",
|
||||
"Instructions": {
|
||||
"Opcode": "SelectUnsharded",
|
||||
"Keyspace": {
|
||||
"Name": "main",
|
||||
"Sharded": false
|
||||
},
|
||||
"Query": "select foo.col from foo",
|
||||
"FieldQuery": "select foo.col from foo where 1 != 1"
|
||||
}
|
||||
}
|
||||
|
||||
# implicit table reference for sharded keyspace
|
||||
"select user.foo.col from user.foo"
|
||||
"table foo not found"
|
||||
|
||||
# duplicate symbols
|
||||
"select user.id from user join user"
|
||||
"duplicate symbol: user"
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
"Owner": "user"
|
||||
}
|
||||
},
|
||||
"Classes": {
|
||||
"Tables": {
|
||||
"user": {
|
||||
"ColVindexes": [
|
||||
{
|
||||
|
@ -77,23 +77,14 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"Tables": {
|
||||
"user": "user",
|
||||
"user_extra": "user_extra",
|
||||
"music": "music",
|
||||
"music_extra": "music_extra"
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"Classes": {
|
||||
"Tables": {
|
||||
"main1": {},
|
||||
"seq": {
|
||||
"Type": "Sequence"
|
||||
}
|
||||
},
|
||||
"Tables": {
|
||||
"main1": "",
|
||||
"seq": "seq"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -146,10 +146,6 @@
|
|||
"delete from user where col = (select id from main1)"
|
||||
"unsupported: subqueries in DML"
|
||||
|
||||
# qualified table name in DMLs
|
||||
"update u.user set id = 1"
|
||||
"unsupported: compex table expression in DML"
|
||||
|
||||
# update with no where clause
|
||||
"update user set val = 1"
|
||||
"unsupported: multi-shard where clause in DML"
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
"Type": "numeric"
|
||||
}
|
||||
},
|
||||
"Classes": {
|
||||
"Tables": {
|
||||
"user": {
|
||||
"ColVindexes": [
|
||||
{
|
||||
|
@ -89,26 +89,18 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"Tables": {
|
||||
"user": "user",
|
||||
"user_extra": "user_extra",
|
||||
"music": "music",
|
||||
"music_extra": "music_extra",
|
||||
"music_user_idx": "music_user_idx"
|
||||
}
|
||||
},
|
||||
"lookup": {
|
||||
"Sharded": false,
|
||||
"Classes": {
|
||||
"seq": {
|
||||
"Type": "Sequence"
|
||||
}
|
||||
},
|
||||
"Tables": {
|
||||
"user_seq": "seq",
|
||||
"music_seq": "seq",
|
||||
"name_user_idx": ""
|
||||
"user_seq": {
|
||||
"Type": "Sequence"
|
||||
},
|
||||
"music_seq": {
|
||||
"Type": "Sequence"
|
||||
},
|
||||
"name_user_idx": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -362,6 +362,36 @@ func (itc *internalTabletConn) SplitQuery(ctx context.Context, query querytypes.
|
|||
return splits, nil
|
||||
}
|
||||
|
||||
// SplitQueryV2 is part of tabletconn.TabletConn
|
||||
// TODO(erez): Rename to SplitQuery once the migration to SplitQuery V2 is done.
|
||||
func (itc *internalTabletConn) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
query querytypes.BoundQuery,
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) ([]querytypes.QuerySplit, error) {
|
||||
|
||||
splits, err := itc.tablet.qsc.QueryService().SplitQueryV2(
|
||||
ctx,
|
||||
&querypb.Target{
|
||||
Keyspace: itc.tablet.keyspace,
|
||||
Shard: itc.tablet.shard,
|
||||
TabletType: itc.tablet.tabletType,
|
||||
},
|
||||
query.Sql,
|
||||
query.BindVariables,
|
||||
splitColumns,
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm,
|
||||
0 /* SessionID */)
|
||||
if err != nil {
|
||||
return nil, tabletconn.TabletErrorFromGRPC(tabletserver.ToGRPCError(err))
|
||||
}
|
||||
return splits, nil
|
||||
}
|
||||
|
||||
type streamHealthReader struct {
|
||||
c <-chan *querypb.StreamHealthResponse
|
||||
err *error
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/youtube/vitess/go/vt/callerid"
|
||||
"github.com/youtube/vitess/go/vt/vtgate/vtgateservice"
|
||||
|
||||
querypb "github.com/youtube/vitess/go/vt/proto/query"
|
||||
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
|
||||
vtgatepb "github.com/youtube/vitess/go/vt/proto/vtgate"
|
||||
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
|
||||
|
@ -64,11 +65,11 @@ func (c *callerIDClient) checkCallerID(ctx context.Context, received string) (bo
|
|||
return true, fmt.Errorf("SUCCESS: callerid matches")
|
||||
}
|
||||
|
||||
func (c *callerIDClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
func (c *callerIDClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
if ok, err := c.checkCallerID(ctx, sql); ok {
|
||||
return nil, err
|
||||
}
|
||||
return c.fallbackClient.Execute(ctx, sql, bindVariables, tabletType, session, notInTransaction)
|
||||
return c.fallbackClient.Execute(ctx, sql, bindVariables, keyspace, tabletType, session, notInTransaction)
|
||||
}
|
||||
|
||||
func (c *callerIDClient) ExecuteShards(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, shards []string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
|
@ -117,11 +118,11 @@ func (c *callerIDClient) ExecuteBatchKeyspaceIds(ctx context.Context, queries []
|
|||
return c.fallbackClient.ExecuteBatchKeyspaceIds(ctx, queries, tabletType, asTransaction, session)
|
||||
}
|
||||
|
||||
func (c *callerIDClient) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
func (c *callerIDClient) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
if ok, err := c.checkCallerID(ctx, sql); ok {
|
||||
return err
|
||||
}
|
||||
return c.fallbackClient.StreamExecute(ctx, sql, bindVariables, tabletType, sendReply)
|
||||
return c.fallbackClient.StreamExecute(ctx, sql, bindVariables, keyspace, tabletType, sendReply)
|
||||
}
|
||||
|
||||
func (c *callerIDClient) StreamExecuteShards(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, shards []string, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
|
@ -151,3 +152,27 @@ func (c *callerIDClient) SplitQuery(ctx context.Context, keyspace string, sql st
|
|||
}
|
||||
return c.fallbackClient.SplitQuery(ctx, sql, keyspace, bindVariables, splitColumn, splitCount)
|
||||
}
|
||||
|
||||
// TODO(erez): Rename after migration to SplitQuery V2 is done.
|
||||
func (c *callerIDClient) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
keyspace string,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
if ok, err := c.checkCallerID(ctx, sql); ok {
|
||||
return nil, err
|
||||
}
|
||||
return c.fallbackClient.SplitQueryV2(
|
||||
ctx,
|
||||
sql,
|
||||
keyspace,
|
||||
bindVariables,
|
||||
splitColumns,
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm)
|
||||
}
|
||||
|
|
|
@ -86,18 +86,19 @@ func echoQueryResult(vals map[string]interface{}) *sqltypes.Result {
|
|||
return qr
|
||||
}
|
||||
|
||||
func (c *echoClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
func (c *echoClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
if strings.HasPrefix(sql, EchoPrefix) {
|
||||
return echoQueryResult(map[string]interface{}{
|
||||
"callerId": callerid.EffectiveCallerIDFromContext(ctx),
|
||||
"query": sql,
|
||||
"bindVars": bindVariables,
|
||||
"keyspace": keyspace,
|
||||
"tabletType": tabletType,
|
||||
"session": session,
|
||||
"notInTransaction": notInTransaction,
|
||||
}), nil
|
||||
}
|
||||
return c.fallbackClient.Execute(ctx, sql, bindVariables, tabletType, session, notInTransaction)
|
||||
return c.fallbackClient.Execute(ctx, sql, bindVariables, keyspace, tabletType, session, notInTransaction)
|
||||
}
|
||||
|
||||
func (c *echoClient) ExecuteShards(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, shards []string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
|
@ -205,17 +206,18 @@ func (c *echoClient) ExecuteBatchKeyspaceIds(ctx context.Context, queries []*vtg
|
|||
return c.fallbackClient.ExecuteBatchKeyspaceIds(ctx, queries, tabletType, asTransaction, session)
|
||||
}
|
||||
|
||||
func (c *echoClient) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
func (c *echoClient) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
if strings.HasPrefix(sql, EchoPrefix) {
|
||||
sendReply(echoQueryResult(map[string]interface{}{
|
||||
"callerId": callerid.EffectiveCallerIDFromContext(ctx),
|
||||
"query": sql,
|
||||
"bindVars": bindVariables,
|
||||
"keyspace": keyspace,
|
||||
"tabletType": tabletType,
|
||||
}))
|
||||
return nil
|
||||
}
|
||||
return c.fallbackClient.StreamExecute(ctx, sql, bindVariables, tabletType, sendReply)
|
||||
return c.fallbackClient.StreamExecute(ctx, sql, bindVariables, keyspace, tabletType, sendReply)
|
||||
}
|
||||
|
||||
func (c *echoClient) StreamExecuteShards(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, shards []string, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
|
@ -283,3 +285,43 @@ func (c *echoClient) SplitQuery(ctx context.Context, keyspace string, sql string
|
|||
}
|
||||
return c.fallback.SplitQuery(ctx, sql, keyspace, bindVariables, splitColumn, splitCount)
|
||||
}
|
||||
|
||||
// TODO(erez): Rename after migration to SplitQuery V2 is done.
|
||||
func (c *echoClient) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
keyspace string,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
|
||||
if strings.HasPrefix(sql, EchoPrefix) {
|
||||
bv, err := querytypes.BindVariablesToProto3(bindVariables)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []*vtgatepb.SplitQueryResponse_Part{
|
||||
{
|
||||
Query: &querypb.BoundQuery{
|
||||
Sql: fmt.Sprintf("%v:%v:%v:%v:%v",
|
||||
sql, splitColumns, splitCount, numRowsPerQueryPart, algorithm),
|
||||
BindVariables: bv,
|
||||
},
|
||||
KeyRangePart: &vtgatepb.SplitQueryResponse_KeyRangePart{
|
||||
Keyspace: keyspace,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return c.fallback.SplitQueryV2(
|
||||
ctx,
|
||||
sql,
|
||||
keyspace,
|
||||
bindVariables,
|
||||
splitColumns,
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm)
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/youtube/vitess/go/vt/vterrors"
|
||||
"github.com/youtube/vitess/go/vt/vtgate/vtgateservice"
|
||||
|
||||
querypb "github.com/youtube/vitess/go/vt/proto/query"
|
||||
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
|
||||
vtgatepb "github.com/youtube/vitess/go/vt/proto/vtgate"
|
||||
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
|
||||
|
@ -117,14 +118,14 @@ func trimmedRequestToError(received string) error {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *errorClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
func (c *errorClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
if err := requestToPartialError(sql, session); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := requestToError(sql); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.fallbackClient.Execute(ctx, sql, bindVariables, tabletType, session, notInTransaction)
|
||||
return c.fallbackClient.Execute(ctx, sql, bindVariables, keyspace, tabletType, session, notInTransaction)
|
||||
}
|
||||
|
||||
func (c *errorClient) ExecuteShards(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, shards []string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
|
@ -191,11 +192,11 @@ func (c *errorClient) ExecuteBatchKeyspaceIds(ctx context.Context, queries []*vt
|
|||
return c.fallbackClient.ExecuteBatchKeyspaceIds(ctx, queries, tabletType, asTransaction, session)
|
||||
}
|
||||
|
||||
func (c *errorClient) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
func (c *errorClient) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
if err := requestToError(sql); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.fallbackClient.StreamExecute(ctx, sql, bindVariables, tabletType, sendReply)
|
||||
return c.fallbackClient.StreamExecute(ctx, sql, bindVariables, keyspace, tabletType, sendReply)
|
||||
}
|
||||
|
||||
func (c *errorClient) StreamExecuteShards(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, shards []string, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
|
@ -256,6 +257,31 @@ func (c *errorClient) SplitQuery(ctx context.Context, keyspace string, sql strin
|
|||
return c.fallbackClient.SplitQuery(ctx, sql, keyspace, bindVariables, splitColumn, splitCount)
|
||||
}
|
||||
|
||||
// TODO(erez): Rename after migration to SplitQuery V2 is done.
|
||||
func (c *errorClient) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
keyspace string,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
|
||||
if err := requestToError(sql); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.fallbackClient.SplitQueryV2(
|
||||
ctx,
|
||||
sql,
|
||||
keyspace,
|
||||
bindVariables,
|
||||
splitColumns,
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm)
|
||||
}
|
||||
|
||||
func (c *errorClient) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) {
|
||||
if err := requestToError(keyspace); err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/youtube/vitess/go/sqltypes"
|
||||
"github.com/youtube/vitess/go/vt/vtgate/vtgateservice"
|
||||
|
||||
querypb "github.com/youtube/vitess/go/vt/proto/query"
|
||||
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
|
||||
vtgatepb "github.com/youtube/vitess/go/vt/proto/vtgate"
|
||||
)
|
||||
|
@ -26,8 +27,8 @@ func newFallbackClient(fallback vtgateservice.VTGateService) fallbackClient {
|
|||
return fallbackClient{fallback: fallback}
|
||||
}
|
||||
|
||||
func (c fallbackClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
return c.fallback.Execute(ctx, sql, bindVariables, tabletType, session, notInTransaction)
|
||||
func (c fallbackClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
return c.fallback.Execute(ctx, sql, bindVariables, keyspace, tabletType, session, notInTransaction)
|
||||
}
|
||||
|
||||
func (c fallbackClient) ExecuteShards(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, shards []string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
|
@ -54,8 +55,8 @@ func (c fallbackClient) ExecuteBatchKeyspaceIds(ctx context.Context, queries []*
|
|||
return c.fallback.ExecuteBatchKeyspaceIds(ctx, queries, tabletType, asTransaction, session)
|
||||
}
|
||||
|
||||
func (c fallbackClient) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
return c.fallback.StreamExecute(ctx, sql, bindVariables, tabletType, sendReply)
|
||||
func (c fallbackClient) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
return c.fallback.StreamExecute(ctx, sql, bindVariables, keyspace, tabletType, sendReply)
|
||||
}
|
||||
|
||||
func (c fallbackClient) StreamExecuteShards(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, shards []string, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
|
@ -86,6 +87,21 @@ func (c fallbackClient) SplitQuery(ctx context.Context, keyspace string, sql str
|
|||
return c.fallback.SplitQuery(ctx, sql, keyspace, bindVariables, splitColumn, splitCount)
|
||||
}
|
||||
|
||||
// TODO(erez): Rename after migration to SplitQuery V2 is done.
|
||||
func (c fallbackClient) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
keyspace string,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm,
|
||||
) ([]*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
return c.fallback.SplitQueryV2(
|
||||
ctx, sql, keyspace, bindVariables, splitColumns, splitCount, numRowsPerQueryPart, algorithm)
|
||||
}
|
||||
|
||||
func (c fallbackClient) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) {
|
||||
return c.fallback.GetSrvKeyspace(ctx, keyspace)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/youtube/vitess/go/tb"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
querypb "github.com/youtube/vitess/go/vt/proto/query"
|
||||
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
|
||||
vtgatepb "github.com/youtube/vitess/go/vt/proto/vtgate"
|
||||
)
|
||||
|
@ -28,7 +29,7 @@ func newTerminalClient() *terminalClient {
|
|||
return &terminalClient{}
|
||||
}
|
||||
|
||||
func (c *terminalClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
func (c *terminalClient) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
if sql == "quit://" {
|
||||
log.Fatal("Received quit:// query. Going down.")
|
||||
}
|
||||
|
@ -59,7 +60,7 @@ func (c *terminalClient) ExecuteBatchKeyspaceIds(ctx context.Context, queries []
|
|||
return nil, errTerminal
|
||||
}
|
||||
|
||||
func (c *terminalClient) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
func (c *terminalClient) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
return errTerminal
|
||||
}
|
||||
|
||||
|
@ -91,6 +92,20 @@ func (c *terminalClient) SplitQuery(ctx context.Context, keyspace string, sql st
|
|||
return nil, errTerminal
|
||||
}
|
||||
|
||||
// TODO(erez): Rename after migration to SplitQuery V2 is done.
|
||||
func (c *terminalClient) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
keyspace string,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm,
|
||||
) ([]*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
return nil, errTerminal
|
||||
}
|
||||
|
||||
func (c *terminalClient) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) {
|
||||
return nil, errTerminal
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ package stats
|
|||
|
||||
import (
|
||||
"expvar"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
@ -122,6 +123,7 @@ func BenchmarkCounters(b *testing.B) {
|
|||
func BenchmarkCountersTailLatency(b *testing.B) {
|
||||
// For this one, ignore the time reported by 'go test'.
|
||||
// The 99th Percentile log line is all that matters.
|
||||
// (Cmd: go test -bench=BenchmarkCountersTailLatency -benchtime=30s -cpu=10)
|
||||
clear()
|
||||
benchCounter.Add("c1", 1)
|
||||
c := make(chan time.Duration, 100)
|
||||
|
@ -140,11 +142,15 @@ func BenchmarkCountersTailLatency(b *testing.B) {
|
|||
}()
|
||||
|
||||
b.ResetTimer()
|
||||
b.SetParallelism(1000)
|
||||
b.SetParallelism(100) // The actual number of goroutines is 100*GOMAXPROCS
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
var start time.Time
|
||||
|
||||
for pb.Next() {
|
||||
// sleep between 0~200ms to simulate 10 QPS per goroutine.
|
||||
time.Sleep(time.Duration(r.Int63n(200)) * time.Millisecond)
|
||||
start = time.Now()
|
||||
benchCounter.Add("c1", 1)
|
||||
c <- time.Since(start)
|
||||
|
|
|
@ -362,6 +362,18 @@ func (fc *fakeConn) SplitQuery(ctx context.Context, query querytypes.BoundQuery,
|
|||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
// SplitQueryV2 implements tabletconn.TabletConn.
|
||||
func (fc *fakeConn) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
query querytypes.BoundQuery,
|
||||
splitColumn []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm,
|
||||
) ([]querytypes.QuerySplit, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
// SetTarget implements tabletconn.TabletConn.
|
||||
func (fc *fakeConn) SetTarget(keyspace, shard string, tabletType topodatapb.TabletType) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
|
|
|
@ -255,6 +255,29 @@ func (x Type) String() string {
|
|||
}
|
||||
func (Type) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
type SplitQueryRequest_Algorithm int32
|
||||
|
||||
const (
|
||||
SplitQueryRequest_EQUAL_SPLITS SplitQueryRequest_Algorithm = 0
|
||||
SplitQueryRequest_FULL_SCAN SplitQueryRequest_Algorithm = 1
|
||||
)
|
||||
|
||||
var SplitQueryRequest_Algorithm_name = map[int32]string{
|
||||
0: "EQUAL_SPLITS",
|
||||
1: "FULL_SCAN",
|
||||
}
|
||||
var SplitQueryRequest_Algorithm_value = map[string]int32{
|
||||
"EQUAL_SPLITS": 0,
|
||||
"FULL_SCAN": 1,
|
||||
}
|
||||
|
||||
func (x SplitQueryRequest_Algorithm) String() string {
|
||||
return proto.EnumName(SplitQueryRequest_Algorithm_name, int32(x))
|
||||
}
|
||||
func (SplitQueryRequest_Algorithm) EnumDescriptor() ([]byte, []int) {
|
||||
return fileDescriptor0, []int{22, 0}
|
||||
}
|
||||
|
||||
// Target describes what the client expects the tablet is.
|
||||
// If the tablet does not match, an error is returned.
|
||||
type Target struct {
|
||||
|
@ -749,15 +772,28 @@ func (m *RollbackResponse) String() string { return proto.CompactText
|
|||
func (*RollbackResponse) ProtoMessage() {}
|
||||
func (*RollbackResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} }
|
||||
|
||||
// SplitQueryRequest is the payload for SplitQuery
|
||||
// SplitQueryRequest is the payload for SplitQuery sent by VTGate to a VTTablet.
|
||||
// See vtgate.SplitQueryRequest for more details.
|
||||
type SplitQueryRequest struct {
|
||||
EffectiveCallerId *vtrpc.CallerID `protobuf:"bytes,1,opt,name=effective_caller_id,json=effectiveCallerId" json:"effective_caller_id,omitempty"`
|
||||
ImmediateCallerId *VTGateCallerID `protobuf:"bytes,2,opt,name=immediate_caller_id,json=immediateCallerId" json:"immediate_caller_id,omitempty"`
|
||||
Target *Target `protobuf:"bytes,3,opt,name=target" json:"target,omitempty"`
|
||||
Query *BoundQuery `protobuf:"bytes,4,opt,name=query" json:"query,omitempty"`
|
||||
SplitColumn string `protobuf:"bytes,5,opt,name=split_column,json=splitColumn" json:"split_column,omitempty"`
|
||||
SplitCount int64 `protobuf:"varint,6,opt,name=split_count,json=splitCount" json:"split_count,omitempty"`
|
||||
SessionId int64 `protobuf:"varint,7,opt,name=session_id,json=sessionId" json:"session_id,omitempty"`
|
||||
SplitColumn []string `protobuf:"bytes,5,rep,name=split_column,json=splitColumn" json:"split_column,omitempty"`
|
||||
// Exactly one of the following must be nonzero.
|
||||
SplitCount int64 `protobuf:"varint,6,opt,name=split_count,json=splitCount" json:"split_count,omitempty"`
|
||||
NumRowsPerQueryPart int64 `protobuf:"varint,8,opt,name=num_rows_per_query_part,json=numRowsPerQueryPart" json:"num_rows_per_query_part,omitempty"`
|
||||
SessionId int64 `protobuf:"varint,7,opt,name=session_id,json=sessionId" json:"session_id,omitempty"`
|
||||
Algorithm SplitQueryRequest_Algorithm `protobuf:"varint,9,opt,name=algorithm,enum=query.SplitQueryRequest_Algorithm" json:"algorithm,omitempty"`
|
||||
// Whether to use the new split-query code
|
||||
// that supports multiple split-columns and
|
||||
// the FULL_SCAN algorithm.
|
||||
// This is a temporary field which aids
|
||||
// in the migration of SplitQuery to the new
|
||||
// code.
|
||||
// TODO(erez): Remove this field after the migration
|
||||
// to the SplitQuery version 2.
|
||||
UseSplitQueryV2 bool `protobuf:"varint,10,opt,name=use_split_query_v2,json=useSplitQueryV2" json:"use_split_query_v2,omitempty"`
|
||||
}
|
||||
|
||||
func (m *SplitQueryRequest) Reset() { *m = SplitQueryRequest{} }
|
||||
|
@ -945,104 +981,112 @@ func init() {
|
|||
proto.RegisterType((*StreamHealthResponse)(nil), "query.StreamHealthResponse")
|
||||
proto.RegisterEnum("query.Flag", Flag_name, Flag_value)
|
||||
proto.RegisterEnum("query.Type", Type_name, Type_value)
|
||||
proto.RegisterEnum("query.SplitQueryRequest_Algorithm", SplitQueryRequest_Algorithm_name, SplitQueryRequest_Algorithm_value)
|
||||
}
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 1523 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe4, 0x58, 0xdb, 0x52, 0x1b, 0x47,
|
||||
0x13, 0xfe, 0x57, 0x27, 0x44, 0x0b, 0x89, 0x65, 0x80, 0xbf, 0x28, 0xfe, 0x43, 0x9c, 0xb5, 0xe3,
|
||||
0x38, 0xc4, 0x45, 0x11, 0xf9, 0x10, 0x57, 0xce, 0x12, 0x2c, 0x58, 0x15, 0x10, 0x78, 0xb4, 0xa2,
|
||||
0xe2, 0xdc, 0x6c, 0x2d, 0xd2, 0x00, 0x5b, 0xac, 0xb4, 0x62, 0x77, 0x84, 0xcd, 0x1d, 0x49, 0x1c,
|
||||
0xe7, 0xe8, 0x1c, 0x2a, 0x71, 0x8e, 0x97, 0xb9, 0xcf, 0x43, 0xe4, 0x01, 0x92, 0x17, 0x48, 0xe5,
|
||||
0x3e, 0x57, 0x79, 0x85, 0xcc, 0x69, 0x97, 0x15, 0xe0, 0x50, 0xe5, 0xab, 0x38, 0xbe, 0xd2, 0x74,
|
||||
0x7f, 0x3d, 0xd3, 0xdd, 0x5f, 0xf7, 0xcc, 0xec, 0x08, 0x0a, 0xbb, 0x7d, 0x12, 0xec, 0xcf, 0xf6,
|
||||
0x02, 0x9f, 0xfa, 0x28, 0x2b, 0x84, 0xe9, 0x12, 0xf5, 0x7b, 0x7e, 0xdb, 0xa1, 0x8e, 0x54, 0x4f,
|
||||
0x17, 0xf6, 0x68, 0xd0, 0x6b, 0x49, 0xc1, 0xd8, 0x85, 0x9c, 0xe5, 0x04, 0x5b, 0x84, 0xa2, 0x69,
|
||||
0xc8, 0xef, 0x90, 0xfd, 0xb0, 0xe7, 0xb4, 0xc8, 0x94, 0x76, 0x46, 0xbb, 0x30, 0x8c, 0x63, 0x19,
|
||||
0x4d, 0x40, 0x36, 0xdc, 0x76, 0x82, 0xf6, 0x54, 0x4a, 0x00, 0x52, 0x40, 0x57, 0xa0, 0x40, 0x9d,
|
||||
0x0d, 0x8f, 0x50, 0x9b, 0xee, 0xf7, 0xc8, 0x54, 0x9a, 0x61, 0xa5, 0xf2, 0xc4, 0x6c, 0xec, 0xce,
|
||||
0x12, 0xa0, 0xc5, 0x30, 0x0c, 0x34, 0x1e, 0x1b, 0x17, 0xa1, 0xb4, 0x6e, 0x2d, 0x39, 0x94, 0xcc,
|
||||
0x3b, 0x9e, 0x47, 0x82, 0xda, 0x02, 0x77, 0xdd, 0x0f, 0x49, 0xd0, 0x75, 0x3a, 0xb1, 0xeb, 0x48,
|
||||
0x36, 0x5e, 0x81, 0xec, 0xba, 0xe3, 0xf5, 0x09, 0x7a, 0x02, 0x32, 0xc2, 0x8d, 0x26, 0xdc, 0x14,
|
||||
0x66, 0x65, 0xa6, 0x62, 0x75, 0x01, 0xf0, 0x20, 0xf7, 0xb8, 0xa5, 0x08, 0x72, 0x04, 0x4b, 0xc1,
|
||||
0xd8, 0x81, 0x91, 0xaa, 0xdb, 0x6d, 0xaf, 0x3b, 0x81, 0xcb, 0x43, 0x78, 0xc8, 0x65, 0xd0, 0x39,
|
||||
0xc8, 0x89, 0x41, 0xc8, 0xd2, 0x4c, 0x5f, 0x28, 0x94, 0x47, 0xd4, 0x44, 0x11, 0x1b, 0x56, 0x98,
|
||||
0xf1, 0x93, 0x06, 0x50, 0xf5, 0xfb, 0xdd, 0xf6, 0x0d, 0x0e, 0x22, 0x1d, 0xd2, 0xe1, 0xae, 0xa7,
|
||||
0x52, 0xe2, 0x43, 0xf4, 0x3a, 0x94, 0x36, 0x58, 0x34, 0xf6, 0x9e, 0x0a, 0x27, 0x64, 0x5e, 0xf8,
|
||||
0x72, 0xe7, 0xd4, 0x72, 0x87, 0x93, 0x67, 0x93, 0x51, 0x87, 0x66, 0x97, 0x06, 0xfb, 0xb8, 0xb8,
|
||||
0x91, 0xd4, 0x4d, 0x37, 0x01, 0x1d, 0x37, 0xe2, 0x4e, 0x59, 0xdd, 0x22, 0xa7, 0x6c, 0x88, 0x9e,
|
||||
0x49, 0x66, 0x54, 0x28, 0x8f, 0x47, 0xbe, 0x12, 0x73, 0x55, 0x9a, 0x2f, 0xa4, 0xae, 0x69, 0xc6,
|
||||
0x4b, 0x90, 0x5d, 0x74, 0x89, 0xd7, 0x46, 0x08, 0x32, 0x89, 0x92, 0x88, 0x71, 0x4c, 0x5f, 0xea,
|
||||
0x01, 0xf4, 0x19, 0xcf, 0x43, 0x1a, 0xfb, 0xb7, 0xd0, 0x14, 0x0c, 0x79, 0xa4, 0xbb, 0x45, 0xb7,
|
||||
0x43, 0x36, 0x3d, 0x7d, 0x01, 0xe1, 0x48, 0x44, 0xff, 0x8e, 0x99, 0x94, 0x04, 0x47, 0xdc, 0xdd,
|
||||
0xd7, 0xa0, 0x20, 0x32, 0xc7, 0x24, 0xec, 0x7b, 0x94, 0x33, 0xbe, 0xc9, 0xc3, 0x90, 0x0b, 0x1c,
|
||||
0x32, 0x2e, 0x62, 0xc3, 0x0a, 0x43, 0x67, 0xa1, 0x18, 0xf8, 0xb7, 0x42, 0xdb, 0xd9, 0xdc, 0x24,
|
||||
0x2d, 0x4a, 0x64, 0x87, 0x66, 0xf0, 0x08, 0x57, 0x56, 0x94, 0x0e, 0xfd, 0x07, 0x86, 0xdd, 0x2e,
|
||||
0x6b, 0x28, 0x6a, 0xbb, 0x6d, 0xd1, 0xa6, 0x19, 0x9c, 0x97, 0x8a, 0x5a, 0x1b, 0xfd, 0x1f, 0x32,
|
||||
0xdc, 0x78, 0x2a, 0x23, 0xbc, 0x80, 0xf2, 0xc2, 0x72, 0xc0, 0x42, 0x6f, 0xfc, 0xa2, 0xc1, 0xf8,
|
||||
0x12, 0xa1, 0x0d, 0x12, 0x86, 0xae, 0xdf, 0xad, 0xb5, 0x31, 0x61, 0x16, 0x21, 0x45, 0xaf, 0xc2,
|
||||
0x38, 0x11, 0x0e, 0xdc, 0x3d, 0x62, 0xb7, 0x44, 0x2b, 0xf3, 0xe5, 0x35, 0xc1, 0xf1, 0xe8, 0xac,
|
||||
0xdc, 0x64, 0x51, 0x8b, 0xe3, 0xb1, 0xd8, 0x56, 0xa9, 0xda, 0xc8, 0x84, 0x71, 0xb7, 0xd3, 0x21,
|
||||
0x6d, 0x97, 0x6d, 0x85, 0xc4, 0x02, 0xb2, 0x48, 0x93, 0x51, 0x7f, 0x0d, 0xec, 0x14, 0x3c, 0x16,
|
||||
0xcf, 0x88, 0x97, 0x49, 0xee, 0xdb, 0xf4, 0x83, 0xf6, 0x6d, 0x26, 0xb1, 0x6f, 0x8d, 0x2b, 0x30,
|
||||
0x31, 0x98, 0x50, 0xd8, 0xf3, 0x19, 0x1b, 0xe8, 0x7f, 0x00, 0xa1, 0x54, 0x46, 0x89, 0xa4, 0xf1,
|
||||
0x70, 0x18, 0x99, 0x19, 0x3f, 0xa6, 0xa0, 0x64, 0xde, 0x26, 0xad, 0x3e, 0x25, 0x7f, 0x37, 0x0e,
|
||||
0x9e, 0x82, 0x1c, 0x15, 0xa7, 0x98, 0x60, 0xa0, 0x50, 0x2e, 0x46, 0x7d, 0x29, 0x94, 0x58, 0x81,
|
||||
0xe8, 0x69, 0x90, 0x47, 0xa2, 0xa0, 0xa3, 0x50, 0x1e, 0x3b, 0xb6, 0xe9, 0xb0, 0xc4, 0xd9, 0x7a,
|
||||
0x25, 0x1a, 0x38, 0xdd, 0xd0, 0x61, 0xd1, 0x4a, 0x36, 0xb2, 0x82, 0x8d, 0x62, 0x42, 0xcb, 0xdc,
|
||||
0x0e, 0x12, 0x96, 0x3b, 0x4a, 0xd8, 0xcb, 0x30, 0x1a, 0xf3, 0xa5, 0x28, 0x9e, 0x81, 0x5c, 0x20,
|
||||
0xda, 0x5b, 0x71, 0x84, 0x54, 0x08, 0x89, 0xc6, 0xc7, 0xca, 0xc2, 0xf8, 0x2d, 0x05, 0xe3, 0x6a,
|
||||
0x7e, 0xd5, 0xa1, 0xad, 0xed, 0x47, 0x94, 0xf4, 0x67, 0x61, 0x88, 0xeb, 0x5d, 0x12, 0x6d, 0xb1,
|
||||
0x13, 0x68, 0x8f, 0x2c, 0x38, 0xf1, 0x4e, 0x68, 0x27, 0x58, 0x16, 0xc4, 0xe7, 0x71, 0xd1, 0x09,
|
||||
0xad, 0x43, 0xe5, 0x09, 0xf5, 0xc9, 0x9d, 0x5e, 0x9f, 0xa1, 0xa3, 0xf5, 0x59, 0x80, 0x89, 0x41,
|
||||
0x7e, 0x55, 0x91, 0x2e, 0xc2, 0x90, 0x2c, 0x41, 0x74, 0xf4, 0x9c, 0x54, 0xa5, 0xc8, 0xc4, 0xb8,
|
||||
0x97, 0x82, 0x89, 0x06, 0x0d, 0x88, 0xd3, 0x79, 0x4c, 0x36, 0xc7, 0x20, 0xab, 0xd9, 0xa3, 0xac,
|
||||
0xce, 0xc3, 0xe4, 0x11, 0x3a, 0x1e, 0xa2, 0xf7, 0x7f, 0xd5, 0xd8, 0xb5, 0x4d, 0xb6, 0xdc, 0xee,
|
||||
0x23, 0x4a, 0xe6, 0x20, 0x47, 0x99, 0xa3, 0x1c, 0x5d, 0x85, 0xa2, 0xca, 0x4e, 0x71, 0x73, 0xbc,
|
||||
0xa1, 0xb5, 0x13, 0x1a, 0xda, 0xb8, 0x93, 0x82, 0xe2, 0xbc, 0xdf, 0xe9, 0xb8, 0xf4, 0x11, 0xe5,
|
||||
0xe5, 0x78, 0x9e, 0x99, 0xd3, 0x37, 0xee, 0xb1, 0x16, 0xd3, 0xa1, 0x14, 0xb1, 0x20, 0xf9, 0x33,
|
||||
0xee, 0xa6, 0x60, 0x14, 0xfb, 0x9e, 0xb7, 0xe1, 0xb4, 0x76, 0x1e, 0x6b, 0x6a, 0x10, 0xe8, 0x87,
|
||||
0x3c, 0x28, 0x72, 0x7e, 0x4e, 0xc1, 0x58, 0xa3, 0xe7, 0xb9, 0x54, 0xed, 0xb4, 0x7f, 0xf6, 0xf1,
|
||||
0xf4, 0x24, 0x8c, 0x84, 0x3c, 0x59, 0xbb, 0xe5, 0x7b, 0xfd, 0x8e, 0xbc, 0x40, 0x86, 0x71, 0x41,
|
||||
0xe8, 0xe6, 0x85, 0x8a, 0x7d, 0xc4, 0x16, 0x22, 0x93, 0x7e, 0x97, 0xaa, 0xbb, 0x03, 0x94, 0x05,
|
||||
0xd3, 0x9c, 0x76, 0x71, 0x60, 0x00, 0xe1, 0x52, 0x90, 0x7a, 0x18, 0x99, 0x76, 0x4a, 0x64, 0xec,
|
||||
0x33, 0x94, 0x7d, 0x51, 0x2a, 0xa7, 0x29, 0xb1, 0x68, 0x9e, 0x29, 0x84, 0x4b, 0xa3, 0x02, 0x28,
|
||||
0x59, 0x23, 0x75, 0x2e, 0x24, 0x2e, 0x4f, 0x6d, 0xe0, 0xf2, 0x3c, 0xf4, 0x1f, 0x5f, 0x9e, 0xc6,
|
||||
0x24, 0x8c, 0xcb, 0x93, 0xf7, 0x3a, 0x71, 0x3c, 0x1a, 0x7d, 0x2f, 0x18, 0x3f, 0xb0, 0x43, 0x03,
|
||||
0x73, 0x8d, 0xdb, 0x21, 0x0d, 0xea, 0xd0, 0x90, 0x53, 0xb4, 0x2d, 0x4c, 0x6c, 0x12, 0x04, 0x7e,
|
||||
0xa0, 0x3e, 0xf0, 0x0b, 0x52, 0x67, 0x72, 0x15, 0x2a, 0xc3, 0x64, 0x48, 0x5a, 0x7e, 0xb7, 0x1d,
|
||||
0xda, 0x1b, 0x64, 0x9b, 0x3f, 0x59, 0x3a, 0x4e, 0x48, 0x49, 0x20, 0xe2, 0x2e, 0xe2, 0x71, 0x05,
|
||||
0x56, 0x05, 0xb6, 0x22, 0x20, 0x34, 0x07, 0x13, 0xec, 0x81, 0xe2, 0xf9, 0x5b, 0x76, 0xcf, 0x73,
|
||||
0xf6, 0x49, 0x10, 0xaa, 0x54, 0x79, 0x5d, 0xb3, 0x18, 0x49, 0x6c, 0x4d, 0x42, 0x92, 0xe7, 0x37,
|
||||
0x61, 0xe6, 0x44, 0x2f, 0xf6, 0xa6, 0xeb, 0xb1, 0x1f, 0xd2, 0xb6, 0x03, 0xc2, 0x72, 0x6c, 0x39,
|
||||
0xe2, 0x53, 0x40, 0xee, 0x87, 0xf3, 0x27, 0xb8, 0x5e, 0x54, 0xe6, 0xf8, 0xd0, 0x9a, 0xb3, 0xdd,
|
||||
0xea, 0xf5, 0xed, 0x7e, 0xe8, 0x6c, 0x11, 0xd1, 0x04, 0x1a, 0xce, 0x33, 0x45, 0x93, 0xcb, 0xfc,
|
||||
0x91, 0xb4, 0xdb, 0x0b, 0x45, 0xe5, 0x35, 0xcc, 0x87, 0xc6, 0xef, 0x5a, 0x74, 0x8d, 0x47, 0xec,
|
||||
0xc5, 0x47, 0x73, 0xd4, 0x9f, 0xda, 0x5f, 0xf5, 0x27, 0x7b, 0xf0, 0xb0, 0x07, 0xc5, 0x9e, 0xdb,
|
||||
0xdd, 0x12, 0x14, 0xe5, 0x71, 0x24, 0xa2, 0x06, 0x9c, 0x57, 0xcf, 0x64, 0x72, 0x9b, 0xf2, 0x57,
|
||||
0xad, 0xe7, 0xed, 0xf3, 0xbc, 0x9c, 0x80, 0x74, 0xd9, 0xeb, 0xc4, 0xe6, 0x75, 0x09, 0xa9, 0xd3,
|
||||
0xe9, 0x09, 0xa2, 0xd2, 0xf8, 0xac, 0xb4, 0x36, 0x63, 0x63, 0x1c, 0xdb, 0x5a, 0x91, 0x29, 0x7a,
|
||||
0x11, 0x4a, 0x81, 0xaa, 0xa9, 0x1d, 0xf2, 0xa2, 0xaa, 0x7d, 0x31, 0x11, 0xbd, 0x5f, 0x92, 0x05,
|
||||
0xc7, 0xc5, 0x20, 0x29, 0xce, 0xec, 0x40, 0x66, 0xd1, 0x73, 0xb6, 0x50, 0x1e, 0x32, 0xf5, 0xd5,
|
||||
0xba, 0xa9, 0xff, 0x0b, 0x8d, 0x02, 0xd4, 0x1a, 0xb5, 0xba, 0x65, 0x2e, 0xe1, 0xca, 0xb2, 0x7e,
|
||||
0x90, 0x92, 0x8a, 0x66, 0xbd, 0x51, 0x5b, 0xaa, 0x9b, 0x0b, 0xfa, 0x41, 0x06, 0x8d, 0xc0, 0x50,
|
||||
0xad, 0xb1, 0xb8, 0xbc, 0x5a, 0xb1, 0xf4, 0x83, 0x3c, 0x2a, 0x42, 0xbe, 0xd6, 0xb8, 0xd1, 0x5c,
|
||||
0xb5, 0x38, 0xa8, 0xa3, 0x02, 0xe4, 0x6a, 0x0d, 0xcb, 0x7c, 0x83, 0x61, 0x67, 0x24, 0x56, 0xad,
|
||||
0xd5, 0x2b, 0xf8, 0xa6, 0x7e, 0xf0, 0xda, 0xcc, 0x1f, 0x29, 0xc8, 0xf0, 0xf7, 0x21, 0xd3, 0x0f,
|
||||
0xd7, 0x9b, 0xcb, 0xcb, 0xb6, 0x75, 0x73, 0x8d, 0xbb, 0x1c, 0x86, 0x0c, 0x73, 0x78, 0x4d, 0x7f,
|
||||
0x2b, 0x85, 0x00, 0xb2, 0x4d, 0x31, 0x7e, 0x3b, 0xc7, 0xc7, 0x6c, 0xf8, 0xdc, 0x55, 0xfd, 0x9d,
|
||||
0x14, 0x5f, 0xb6, 0x29, 0x85, 0x3b, 0x11, 0x50, 0xbe, 0xac, 0xbf, 0x1b, 0x03, 0x4c, 0xb8, 0x1b,
|
||||
0x01, 0x97, 0xca, 0xfa, 0x7b, 0x31, 0xc0, 0x84, 0xf7, 0x23, 0xe0, 0xea, 0x65, 0xfd, 0x83, 0x18,
|
||||
0x60, 0xc2, 0x87, 0x39, 0x9e, 0x8b, 0xc8, 0x84, 0x99, 0x7d, 0x94, 0x8f, 0x25, 0x86, 0x7d, 0x9c,
|
||||
0x47, 0x25, 0x18, 0xb6, 0x6a, 0x2b, 0x66, 0xc3, 0xaa, 0xac, 0xac, 0xe9, 0xf7, 0x74, 0x1e, 0xe6,
|
||||
0x42, 0xc5, 0x32, 0xf5, 0x4f, 0xc4, 0x90, 0x43, 0xfa, 0xa7, 0x3a, 0xcf, 0x91, 0x6b, 0x85, 0xf8,
|
||||
0x99, 0x40, 0x6e, 0x9a, 0x15, 0xac, 0x7f, 0x9e, 0x63, 0x8e, 0x86, 0x16, 0xcc, 0xf9, 0xda, 0x0a,
|
||||
0xa3, 0x11, 0x89, 0x19, 0x9c, 0x95, 0x2f, 0xe6, 0xf8, 0xb0, 0xba, 0xbc, 0x5a, 0xd5, 0xbf, 0x5c,
|
||||
0xe3, 0x0e, 0xd7, 0x2b, 0x78, 0xfe, 0x3a, 0x9b, 0x70, 0x7f, 0x8e, 0x3b, 0x64, 0x92, 0xe2, 0xeb,
|
||||
0xab, 0x35, 0x6e, 0x28, 0xa0, 0xaf, 0xe7, 0x78, 0xd0, 0x4a, 0xff, 0xcd, 0x1a, 0x2b, 0x56, 0xba,
|
||||
0x5a, 0xb3, 0xf4, 0x6f, 0x85, 0x37, 0xb3, 0xde, 0x5c, 0xd1, 0xbf, 0xd3, 0xb9, 0xb2, 0x61, 0x5a,
|
||||
0xfa, 0xf7, 0x5c, 0x99, 0xb5, 0x9a, 0x6b, 0xcb, 0xa6, 0xfe, 0xdf, 0xea, 0x34, 0x4c, 0xb5, 0xfc,
|
||||
0xce, 0xec, 0xbe, 0xdf, 0xa7, 0xfd, 0x0d, 0x32, 0xbb, 0xe7, 0x52, 0x76, 0x72, 0xc9, 0xff, 0x7b,
|
||||
0x36, 0x72, 0xe2, 0xe7, 0xd2, 0x9f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa3, 0x79, 0x25, 0x07, 0x29,
|
||||
0x12, 0x00, 0x00,
|
||||
// 1641 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe4, 0x58, 0x49, 0x73, 0xdb, 0xc6,
|
||||
0x12, 0x7e, 0xe0, 0x26, 0xb2, 0x29, 0x52, 0xd0, 0x50, 0x7a, 0x8f, 0xa5, 0xb7, 0xf9, 0xc1, 0x7e,
|
||||
0x8e, 0xa3, 0xb8, 0x58, 0x0a, 0xbd, 0xc4, 0x95, 0xd5, 0xa4, 0x04, 0xc9, 0xac, 0x48, 0x14, 0x3d,
|
||||
0x04, 0x55, 0x71, 0x2e, 0x28, 0x88, 0x1c, 0x51, 0x28, 0x81, 0x04, 0x05, 0x0c, 0x65, 0xeb, 0xa6,
|
||||
0x24, 0x8e, 0xb3, 0x3a, 0x4b, 0x25, 0xce, 0x7a, 0x4a, 0xe5, 0x9e, 0x1f, 0x91, 0x3f, 0x90, 0x3f,
|
||||
0x90, 0xca, 0x3d, 0xa7, 0xfc, 0x85, 0xcc, 0x0c, 0x06, 0x20, 0xb5, 0x38, 0xaa, 0xf2, 0x29, 0x8e,
|
||||
0x4f, 0x9c, 0xee, 0xaf, 0x67, 0x7a, 0xfa, 0xeb, 0x6e, 0xcc, 0x0c, 0x21, 0xbb, 0x3b, 0x24, 0xde,
|
||||
0x7e, 0x69, 0xe0, 0xb9, 0xd4, 0x45, 0x49, 0x21, 0xcc, 0xe5, 0xa9, 0x3b, 0x70, 0x3b, 0x16, 0xb5,
|
||||
0x02, 0xf5, 0x5c, 0x76, 0x8f, 0x7a, 0x83, 0x76, 0x20, 0x68, 0xbb, 0x90, 0x32, 0x2c, 0xaf, 0x4b,
|
||||
0x28, 0x9a, 0x83, 0xf4, 0x0e, 0xd9, 0xf7, 0x07, 0x56, 0x9b, 0x14, 0x95, 0x33, 0xca, 0x85, 0x0c,
|
||||
0x8e, 0x64, 0x34, 0x03, 0x49, 0x7f, 0xdb, 0xf2, 0x3a, 0xc5, 0x98, 0x00, 0x02, 0x01, 0x5d, 0x81,
|
||||
0x2c, 0xb5, 0x36, 0x1d, 0x42, 0x4d, 0xba, 0x3f, 0x20, 0xc5, 0x38, 0xc3, 0xf2, 0xe5, 0x99, 0x52,
|
||||
0xe4, 0xce, 0x10, 0xa0, 0xc1, 0x30, 0x0c, 0x34, 0x1a, 0x6b, 0x17, 0x21, 0xbf, 0x61, 0xac, 0x58,
|
||||
0x94, 0x2c, 0x5a, 0x8e, 0x43, 0xbc, 0xda, 0x12, 0x77, 0x3d, 0xf4, 0x89, 0xd7, 0xb7, 0x7a, 0x91,
|
||||
0xeb, 0x50, 0xd6, 0x5e, 0x86, 0xe4, 0x86, 0xe5, 0x0c, 0x09, 0xfa, 0x2f, 0x24, 0x84, 0x1b, 0x45,
|
||||
0xb8, 0xc9, 0x96, 0x82, 0x48, 0xc5, 0xea, 0x02, 0xe0, 0x9b, 0xdc, 0xe3, 0x96, 0x62, 0x93, 0x93,
|
||||
0x38, 0x10, 0xb4, 0x1d, 0x98, 0xac, 0xda, 0xfd, 0xce, 0x86, 0xe5, 0xd9, 0x7c, 0x0b, 0x8f, 0xb8,
|
||||
0x0c, 0x3a, 0x07, 0x29, 0x31, 0xf0, 0x59, 0x98, 0xf1, 0x0b, 0xd9, 0xf2, 0xa4, 0x9c, 0x28, 0xf6,
|
||||
0x86, 0x25, 0xa6, 0xfd, 0xa8, 0x00, 0x54, 0xdd, 0x61, 0xbf, 0x73, 0x93, 0x83, 0x48, 0x85, 0xb8,
|
||||
0xbf, 0xeb, 0xc8, 0x90, 0xf8, 0x10, 0xbd, 0x0a, 0xf9, 0x4d, 0xb6, 0x1b, 0x73, 0x4f, 0x6e, 0xc7,
|
||||
0x67, 0x5e, 0xf8, 0x72, 0xe7, 0xe4, 0x72, 0xa3, 0xc9, 0xa5, 0xf1, 0x5d, 0xfb, 0x7a, 0x9f, 0x7a,
|
||||
0xfb, 0x38, 0xb7, 0x39, 0xae, 0x9b, 0x6b, 0x01, 0x3a, 0x6e, 0xc4, 0x9d, 0xb2, 0xbc, 0x85, 0x4e,
|
||||
0xd9, 0x10, 0x3d, 0x3d, 0x1e, 0x51, 0xb6, 0x5c, 0x08, 0x7d, 0x8d, 0xcd, 0x95, 0x61, 0x3e, 0x1f,
|
||||
0xbb, 0xa6, 0x68, 0x2f, 0x42, 0x72, 0xd9, 0x26, 0x4e, 0x07, 0x21, 0x48, 0x8c, 0xa5, 0x44, 0x8c,
|
||||
0x23, 0xfa, 0x62, 0x0f, 0xa1, 0x4f, 0x7b, 0x0e, 0xe2, 0xd8, 0xbd, 0x8d, 0x8a, 0x30, 0xe1, 0x90,
|
||||
0x7e, 0x97, 0x6e, 0xfb, 0x6c, 0x7a, 0xfc, 0x02, 0xc2, 0xa1, 0x88, 0xfe, 0x1e, 0x31, 0x19, 0x10,
|
||||
0x1c, 0x72, 0xf7, 0x40, 0x81, 0xac, 0x88, 0x1c, 0x13, 0x7f, 0xe8, 0x50, 0xce, 0xf8, 0x16, 0xdf,
|
||||
0x46, 0xb0, 0xc0, 0x88, 0x71, 0xb1, 0x37, 0x2c, 0x31, 0x74, 0x16, 0x72, 0x9e, 0x7b, 0xdb, 0x37,
|
||||
0xad, 0xad, 0x2d, 0xd2, 0xa6, 0x24, 0xa8, 0xd0, 0x04, 0x9e, 0xe4, 0xca, 0x8a, 0xd4, 0xa1, 0x7f,
|
||||
0x42, 0xc6, 0xee, 0xb3, 0x82, 0xa2, 0xa6, 0xdd, 0x11, 0x65, 0x9a, 0xc0, 0xe9, 0x40, 0x51, 0xeb,
|
||||
0xa0, 0xff, 0x40, 0x82, 0x1b, 0x17, 0x13, 0xc2, 0x0b, 0x48, 0x2f, 0x2c, 0x06, 0x2c, 0xf4, 0xda,
|
||||
0x4f, 0x0a, 0x14, 0x56, 0x08, 0x6d, 0x12, 0xdf, 0xb7, 0xdd, 0x7e, 0xad, 0x83, 0x09, 0xb3, 0xf0,
|
||||
0x29, 0x7a, 0x05, 0x0a, 0x44, 0x38, 0xb0, 0xf7, 0x88, 0xd9, 0x16, 0xa5, 0xcc, 0x97, 0x57, 0x04,
|
||||
0xc7, 0x53, 0xa5, 0xa0, 0xc9, 0xc2, 0x12, 0xc7, 0xd3, 0x91, 0xad, 0x54, 0x75, 0x90, 0x0e, 0x05,
|
||||
0xbb, 0xd7, 0x23, 0x1d, 0x9b, 0xb5, 0xc2, 0xd8, 0x02, 0x41, 0x92, 0x66, 0xc3, 0xfa, 0x3a, 0xd4,
|
||||
0x29, 0x78, 0x3a, 0x9a, 0x11, 0x2d, 0x33, 0xde, 0xb7, 0xf1, 0x87, 0xf5, 0x6d, 0x62, 0xac, 0x6f,
|
||||
0xb5, 0x2b, 0x30, 0x73, 0x38, 0x20, 0x7f, 0xe0, 0x32, 0x36, 0xd0, 0xbf, 0x01, 0xfc, 0x40, 0x19,
|
||||
0x06, 0x12, 0xc7, 0x19, 0x3f, 0x34, 0xd3, 0x7e, 0x88, 0x41, 0x5e, 0xbf, 0x43, 0xda, 0x43, 0x4a,
|
||||
0xfe, 0x6c, 0x1c, 0xfc, 0x1f, 0x52, 0x54, 0x7c, 0xc5, 0x04, 0x03, 0xd9, 0x72, 0x2e, 0xac, 0x4b,
|
||||
0xa1, 0xc4, 0x12, 0x44, 0x4f, 0x41, 0xf0, 0x49, 0x14, 0x74, 0x64, 0xcb, 0xd3, 0xc7, 0x9a, 0x0e,
|
||||
0x07, 0x38, 0x5b, 0x2f, 0x4f, 0x3d, 0xab, 0xef, 0x5b, 0x6c, 0xb7, 0x01, 0x1b, 0x49, 0xc1, 0x46,
|
||||
0x6e, 0x4c, 0xcb, 0xdc, 0x1e, 0x26, 0x2c, 0x75, 0x94, 0xb0, 0x97, 0x60, 0x2a, 0xe2, 0x4b, 0x52,
|
||||
0x3c, 0x0f, 0x29, 0x4f, 0x94, 0xb7, 0xe4, 0x08, 0xc9, 0x2d, 0x8c, 0x15, 0x3e, 0x96, 0x16, 0xda,
|
||||
0x2f, 0x31, 0x28, 0xc8, 0xf9, 0x55, 0x8b, 0xb6, 0xb7, 0x1f, 0x53, 0xd2, 0x9f, 0x81, 0x09, 0xae,
|
||||
0xb7, 0x49, 0xd8, 0x62, 0x27, 0xd0, 0x1e, 0x5a, 0x70, 0xe2, 0x2d, 0xdf, 0x1c, 0x63, 0x59, 0x10,
|
||||
0x9f, 0xc6, 0x39, 0xcb, 0x37, 0x46, 0xca, 0x13, 0xf2, 0x93, 0x3a, 0x3d, 0x3f, 0x13, 0x47, 0xf3,
|
||||
0xb3, 0x04, 0x33, 0x87, 0xf9, 0x95, 0x49, 0xba, 0x08, 0x13, 0x41, 0x0a, 0xc2, 0x4f, 0xcf, 0x49,
|
||||
0x59, 0x0a, 0x4d, 0xb4, 0xfb, 0x31, 0x98, 0x69, 0x52, 0x8f, 0x58, 0xbd, 0x27, 0xa4, 0x39, 0x0e,
|
||||
0xb3, 0x9a, 0x3c, 0xca, 0xea, 0x22, 0xcc, 0x1e, 0xa1, 0xe3, 0x11, 0x6a, 0xff, 0x67, 0x85, 0x1d,
|
||||
0xdb, 0xa4, 0x6b, 0xf7, 0x1f, 0x53, 0x32, 0x0f, 0x73, 0x94, 0x38, 0xca, 0xd1, 0x55, 0xc8, 0xc9,
|
||||
0xe8, 0x24, 0x37, 0xc7, 0x0b, 0x5a, 0x39, 0xa1, 0xa0, 0xb5, 0xbb, 0x31, 0xc8, 0x2d, 0xba, 0xbd,
|
||||
0x9e, 0x4d, 0x1f, 0x53, 0x5e, 0x8e, 0xc7, 0x99, 0x38, 0xbd, 0x71, 0x8f, 0x95, 0x98, 0x0a, 0xf9,
|
||||
0x90, 0x85, 0x80, 0x3f, 0xed, 0x5e, 0x0c, 0xa6, 0xb0, 0xeb, 0x38, 0x9b, 0x56, 0x7b, 0xe7, 0x89,
|
||||
0xa6, 0x06, 0x81, 0x3a, 0xe2, 0x41, 0x92, 0xf3, 0x5d, 0x02, 0xa6, 0x9b, 0x03, 0xc7, 0xa6, 0xb2,
|
||||
0xd3, 0xfe, 0xda, 0x9f, 0xa7, 0xff, 0xc1, 0xa4, 0xcf, 0x83, 0x35, 0xdb, 0xae, 0x33, 0xec, 0xf1,
|
||||
0x03, 0x24, 0xce, 0xae, 0x3e, 0x59, 0xa1, 0x5b, 0x14, 0x2a, 0x76, 0x89, 0xcd, 0x86, 0x26, 0xc3,
|
||||
0x3e, 0x95, 0x67, 0x07, 0x48, 0x0b, 0xa6, 0x41, 0x97, 0xe1, 0x1f, 0xfd, 0x61, 0xcf, 0x14, 0x37,
|
||||
0xcb, 0x01, 0x0b, 0x4b, 0xac, 0x6c, 0x0e, 0x2c, 0x8f, 0x16, 0xd3, 0xc2, 0xb8, 0xc0, 0x60, 0x76,
|
||||
0x45, 0xf4, 0x1b, 0xc4, 0x13, 0xce, 0x1b, 0x0c, 0x3a, 0xe5, 0xb8, 0x41, 0xd7, 0x21, 0x63, 0x39,
|
||||
0x5d, 0xd7, 0xb3, 0xe9, 0x76, 0xaf, 0x98, 0x11, 0xf7, 0x67, 0x4d, 0x46, 0x71, 0x2c, 0x3b, 0xa5,
|
||||
0x4a, 0x68, 0x89, 0x47, 0x93, 0xd8, 0x51, 0x8a, 0xd8, 0xbb, 0xc8, 0x0c, 0xf6, 0x1e, 0xec, 0x69,
|
||||
0xaf, 0x5c, 0x04, 0x71, 0x42, 0x4e, 0x31, 0x64, 0xb4, 0xcc, 0x46, 0x99, 0x3d, 0xb3, 0x32, 0xd1,
|
||||
0x22, 0xec, 0x51, 0x30, 0xa9, 0xdf, 0x6c, 0x55, 0x56, 0xcd, 0x66, 0x63, 0xb5, 0x66, 0x34, 0xd5,
|
||||
0xbf, 0xa1, 0x1c, 0x64, 0x96, 0x5b, 0xab, 0x4c, 0xb1, 0x58, 0xa9, 0xab, 0x8a, 0x86, 0x01, 0xc4,
|
||||
0x44, 0xb1, 0xc4, 0x88, 0x6c, 0xe5, 0x14, 0xb2, 0xd9, 0xcd, 0x9a, 0x91, 0x24, 0x79, 0x8c, 0x89,
|
||||
0x88, 0xd3, 0x4c, 0x21, 0x58, 0xd4, 0x2a, 0x80, 0xc6, 0x03, 0x93, 0x9f, 0xba, 0xb1, 0xfb, 0x80,
|
||||
0x72, 0xe8, 0x3e, 0x30, 0xf2, 0x1f, 0xdd, 0x07, 0xb4, 0x59, 0x28, 0x04, 0x87, 0xc9, 0x0d, 0x62,
|
||||
0x39, 0x34, 0xbc, 0x02, 0x69, 0xdf, 0xb3, 0xef, 0x20, 0xe6, 0x1a, 0xbb, 0x47, 0x9a, 0xd4, 0xa2,
|
||||
0x3e, 0xcf, 0xfa, 0xb6, 0x30, 0x31, 0x89, 0xe7, 0xb9, 0x9e, 0x7c, 0xb3, 0x64, 0x03, 0x9d, 0xce,
|
||||
0x55, 0xa8, 0x0c, 0xb3, 0x3e, 0x69, 0xbb, 0xfd, 0x8e, 0x6f, 0x6e, 0x92, 0x6d, 0xfe, 0x0a, 0xeb,
|
||||
0x59, 0x3e, 0x25, 0x9e, 0xd8, 0x77, 0x0e, 0x17, 0x24, 0x58, 0x15, 0xd8, 0x9a, 0x80, 0xd0, 0x02,
|
||||
0xcc, 0xb0, 0x37, 0x97, 0xe3, 0x76, 0xcd, 0x81, 0x63, 0xed, 0x13, 0xcf, 0x97, 0xa1, 0xf2, 0x52,
|
||||
0x4d, 0x62, 0x14, 0x60, 0x8d, 0x00, 0x0a, 0x4a, 0xe7, 0x75, 0x98, 0x3f, 0xd1, 0x8b, 0xb9, 0x65,
|
||||
0x3b, 0xec, 0x87, 0x74, 0x4c, 0x8f, 0xb0, 0x18, 0xdb, 0x96, 0xb8, 0xdd, 0x04, 0x2d, 0x7e, 0xfe,
|
||||
0x04, 0xd7, 0xcb, 0xd2, 0x1c, 0x8f, 0xac, 0x39, 0xdb, 0xed, 0xc1, 0xd0, 0x1c, 0xfa, 0x56, 0x97,
|
||||
0x88, 0xd6, 0x57, 0x70, 0x9a, 0x29, 0x5a, 0x5c, 0xe6, 0xef, 0xbe, 0xdd, 0x81, 0x2f, 0x8a, 0x59,
|
||||
0xc1, 0x7c, 0xa8, 0xfd, 0xaa, 0x84, 0x37, 0x93, 0x90, 0xbd, 0xe8, 0xb4, 0x09, 0x5b, 0x4e, 0xf9,
|
||||
0xa3, 0x96, 0x63, 0x6f, 0x38, 0xf6, 0x46, 0xda, 0xb3, 0xfb, 0x5d, 0x41, 0x51, 0x1a, 0x87, 0x22,
|
||||
0x6a, 0xc2, 0x79, 0xf9, 0xf2, 0x27, 0x77, 0x28, 0x7f, 0xa8, 0x3b, 0xce, 0x3e, 0x8f, 0xcb, 0xf2,
|
||||
0x48, 0x9f, 0x3d, 0xb8, 0x4c, 0x9e, 0x17, 0x9f, 0x5a, 0xbd, 0x81, 0x20, 0x2a, 0x8e, 0xcf, 0x06,
|
||||
0xd6, 0x7a, 0x64, 0x8c, 0x23, 0x5b, 0x23, 0x34, 0x45, 0x2f, 0x40, 0xde, 0x93, 0x39, 0x35, 0x7d,
|
||||
0x9e, 0x54, 0xd9, 0xea, 0x33, 0xe1, 0x93, 0x6c, 0x3c, 0xe1, 0x38, 0xe7, 0x8d, 0x8b, 0xf3, 0x3b,
|
||||
0x90, 0x58, 0x76, 0xac, 0x2e, 0x4a, 0x43, 0xa2, 0xbe, 0x5e, 0xd7, 0x59, 0x81, 0x4f, 0x01, 0xd4,
|
||||
0x9a, 0xb5, 0xba, 0xa1, 0xaf, 0xe0, 0xca, 0xaa, 0x7a, 0x10, 0x0b, 0x14, 0xad, 0x7a, 0xb3, 0xb6,
|
||||
0x52, 0xd7, 0x97, 0xd4, 0x83, 0x04, 0x9a, 0x84, 0x89, 0x5a, 0x73, 0x79, 0x75, 0xbd, 0x62, 0xa8,
|
||||
0x07, 0x69, 0xd6, 0x10, 0xe9, 0x5a, 0xf3, 0x66, 0x6b, 0xdd, 0xe0, 0xa0, 0x8a, 0xb2, 0x90, 0xaa,
|
||||
0x35, 0x0d, 0xfd, 0x35, 0x86, 0x9d, 0x09, 0xb0, 0x6a, 0xad, 0x5e, 0xc1, 0xb7, 0xd4, 0x83, 0xeb,
|
||||
0xf3, 0xbf, 0xc5, 0x20, 0xc1, 0x9f, 0xbc, 0xbc, 0x89, 0xea, 0xbc, 0x89, 0x8c, 0x5b, 0x0d, 0xee,
|
||||
0x32, 0x03, 0x09, 0xe6, 0xf0, 0x9a, 0xfa, 0x46, 0x0c, 0x01, 0x24, 0x5b, 0x62, 0xfc, 0x66, 0x8a,
|
||||
0x8f, 0xd9, 0xf0, 0xd9, 0xab, 0xea, 0x5b, 0x31, 0xbe, 0x6c, 0x2b, 0x10, 0xee, 0x86, 0x40, 0xf9,
|
||||
0xb2, 0xfa, 0x76, 0x04, 0x30, 0xe1, 0x5e, 0x08, 0x5c, 0x2a, 0xab, 0xef, 0x44, 0x00, 0x13, 0xde,
|
||||
0x0d, 0x81, 0xab, 0x97, 0xd5, 0xf7, 0x22, 0x80, 0x09, 0xef, 0xa7, 0x78, 0x2c, 0x22, 0x12, 0x66,
|
||||
0xf6, 0x41, 0x3a, 0x92, 0x18, 0xf6, 0x61, 0x1a, 0xe5, 0x21, 0x63, 0xd4, 0xd6, 0xf4, 0xa6, 0x51,
|
||||
0x59, 0x6b, 0xa8, 0xf7, 0x55, 0xbe, 0xcd, 0xa5, 0x8a, 0xa1, 0xab, 0x1f, 0x89, 0x21, 0x87, 0xd4,
|
||||
0x8f, 0x55, 0x1e, 0x23, 0xd7, 0x0a, 0xf1, 0x13, 0x81, 0xdc, 0xd2, 0x2b, 0x58, 0xfd, 0x34, 0xc5,
|
||||
0x1c, 0x4d, 0x2c, 0xe9, 0x8b, 0xb5, 0x35, 0x46, 0x23, 0x12, 0x33, 0x38, 0x2b, 0x9f, 0x2d, 0xf0,
|
||||
0x61, 0x75, 0x75, 0xbd, 0xaa, 0x7e, 0xde, 0xe0, 0x0e, 0x37, 0x2a, 0x78, 0xf1, 0x06, 0x9b, 0xf0,
|
||||
0x60, 0x81, 0x3b, 0x64, 0x92, 0xe4, 0xeb, 0x8b, 0x06, 0x37, 0x14, 0xd0, 0x97, 0x0b, 0x7c, 0xd3,
|
||||
0x52, 0xff, 0x55, 0x83, 0x25, 0x2b, 0x5e, 0xad, 0x19, 0xea, 0xd7, 0xc2, 0x9b, 0x5e, 0x6f, 0xad,
|
||||
0xa9, 0xdf, 0xa8, 0x5c, 0xd9, 0xd4, 0x0d, 0xf5, 0x5b, 0xae, 0x4c, 0x1a, 0xad, 0xc6, 0xaa, 0xae,
|
||||
0xfe, 0xab, 0x3a, 0x07, 0xc5, 0xb6, 0xdb, 0x2b, 0xed, 0xbb, 0x43, 0x3a, 0xdc, 0x24, 0xa5, 0x3d,
|
||||
0x9b, 0xb2, 0xcf, 0x6a, 0xf0, 0x17, 0xd6, 0x66, 0x4a, 0xfc, 0x5c, 0xfa, 0x3d, 0x00, 0x00, 0xff,
|
||||
0xff, 0xa4, 0xf3, 0x84, 0xc5, 0xfc, 0x12, 0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -114,6 +114,8 @@ type ExecuteRequest struct {
|
|||
TabletType topodata.TabletType `protobuf:"varint,4,opt,name=tablet_type,json=tabletType,enum=topodata.TabletType" json:"tablet_type,omitempty"`
|
||||
// not_in_transaction is deprecated and should not be used.
|
||||
NotInTransaction bool `protobuf:"varint,5,opt,name=not_in_transaction,json=notInTransaction" json:"not_in_transaction,omitempty"`
|
||||
// keyspace to target the query to.
|
||||
Keyspace string `protobuf:"bytes,6,opt,name=keyspace" json:"keyspace,omitempty"`
|
||||
}
|
||||
|
||||
func (m *ExecuteRequest) Reset() { *m = ExecuteRequest{} }
|
||||
|
@ -774,6 +776,8 @@ type StreamExecuteRequest struct {
|
|||
Query *query.BoundQuery `protobuf:"bytes,2,opt,name=query" json:"query,omitempty"`
|
||||
// tablet_type is the type of tablets that this query is targeted to.
|
||||
TabletType topodata.TabletType `protobuf:"varint,3,opt,name=tablet_type,json=tabletType,enum=topodata.TabletType" json:"tablet_type,omitempty"`
|
||||
// keyspace to target the query to.
|
||||
Keyspace string `protobuf:"bytes,4,opt,name=keyspace" json:"keyspace,omitempty"`
|
||||
}
|
||||
|
||||
func (m *StreamExecuteRequest) Reset() { *m = StreamExecuteRequest{} }
|
||||
|
@ -1102,18 +1106,95 @@ func (*RollbackResponse) ProtoMessage() {}
|
|||
func (*RollbackResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} }
|
||||
|
||||
// SplitQueryRequest is the payload to SplitQuery.
|
||||
//
|
||||
// SplitQuery takes a "SELECT" query and generates a list of queries called
|
||||
// "query-parts". Each query-part consists of the original query with an
|
||||
// added WHERE clause that restricts the query-part to operate only on
|
||||
// rows whose values in the the columns listed in the "split_column" field
|
||||
// of the request (see below) are in a particular range.
|
||||
//
|
||||
// It is guaranteed that the set of rows obtained from
|
||||
// executing each query-part on a database snapshot
|
||||
// and merging (without deduping) the results is equal to the set of rows
|
||||
// obtained from executing the original query on the same snapshot
|
||||
// with the rows containing NULL values in any of the split_column's excluded.
|
||||
//
|
||||
// This is typically called by the MapReduce master when reading from Vitess.
|
||||
// There it's desirable that the sets of rows returned by the query-parts
|
||||
// have roughly the same size.
|
||||
type SplitQueryRequest struct {
|
||||
// caller_id identifies the caller. This is the effective caller ID,
|
||||
// set by the application to further identify the caller.
|
||||
CallerId *vtrpc.CallerID `protobuf:"bytes,1,opt,name=caller_id,json=callerId" json:"caller_id,omitempty"`
|
||||
// keyspace to target the query to.
|
||||
Keyspace string `protobuf:"bytes,2,opt,name=keyspace" json:"keyspace,omitempty"`
|
||||
// query is the query and bind variables to produce splits for.
|
||||
// The query and bind variables to produce splits for.
|
||||
// The given query must be a simple query of the form
|
||||
// SELECT <cols> FROM <table> WHERE <filter>.
|
||||
// It must not contain subqueries nor any of the keywords
|
||||
// JOIN, GROUP BY, ORDER BY, LIMIT, DISTINCT.
|
||||
// Furthermore, <table> must be a single “concrete” table.
|
||||
// It cannot be a view.
|
||||
Query *query.BoundQuery `protobuf:"bytes,3,opt,name=query" json:"query,omitempty"`
|
||||
// split_column is an optional hint on the column to use to split the query.
|
||||
SplitColumn string `protobuf:"bytes,4,opt,name=split_column,json=splitColumn" json:"split_column,omitempty"`
|
||||
// split_count describes how many splits we want for this query.
|
||||
SplitCount int64 `protobuf:"varint,5,opt,name=split_count,json=splitCount" json:"split_count,omitempty"`
|
||||
// Each generated query-part will be restricted to rows whose values
|
||||
// in the columns listed in this field are in a particular range.
|
||||
// The list of columns named here must be a prefix of the list of
|
||||
// columns defining some index or primary key of the table
|
||||
// referenced in 'query'. For many tables using the primary key columns
|
||||
// (in order) is sufficient and this is the default if this field is omitted.
|
||||
// See the comment on the 'algorithm' field for more restrictions and
|
||||
// information.
|
||||
SplitColumn []string `protobuf:"bytes,4,rep,name=split_column,json=splitColumn" json:"split_column,omitempty"`
|
||||
// You can specify either an estimate of the number of query-parts to
|
||||
// generate or an estimate of the number of rows each query-part should
|
||||
// return.
|
||||
// Thus, exactly one of split_count or num_rows_per_query_part
|
||||
// should be nonzero.
|
||||
// The non-given parameter is calculated from the given parameter
|
||||
// using the formula: split_count * num_rows_per_query_pary = table_size,
|
||||
// where table_size is an approximation of the number of rows in the
|
||||
// table.
|
||||
// Note that if "split_count" is given it is regarded as an estimate.
|
||||
// The number of query-parts returned may differ slightly (in particular,
|
||||
// if it's not a whole multiple of the number of vitess shards).
|
||||
SplitCount int64 `protobuf:"varint,5,opt,name=split_count,json=splitCount" json:"split_count,omitempty"`
|
||||
NumRowsPerQueryPart int64 `protobuf:"varint,6,opt,name=num_rows_per_query_part,json=numRowsPerQueryPart" json:"num_rows_per_query_part,omitempty"`
|
||||
// The algorithm to use to split the query. The split algorithm is performed
|
||||
// on each database shard in parallel. The lists of query-parts generated
|
||||
// by the shards are merged and returned to the caller.
|
||||
// Two algorithms are supported:
|
||||
// EQUAL_SPLITS
|
||||
// If this algorithm is used then only one split_column is allowed.
|
||||
// Additionally, the split_column must have numeric type (integral or
|
||||
// floating point). The algorithm works by taking the interval [min, max],
|
||||
// where min and max are the minimum and maximum values of of the
|
||||
// split_column column in the table-shard, respectively, and
|
||||
// partitioning it into split_count sub-intervals of equal size.
|
||||
// The added WHERE clause of each query-part restricts that part to rows
|
||||
// whose value in split_column belongs to a particular sub-interval.
|
||||
// This is fast, but requires that the distribution of values of
|
||||
// split_column be uniform in [min, max], for the number of rows
|
||||
// returned by each query part to be roughly the same.
|
||||
// FULL_SCAN
|
||||
// If this algorithm is used then the split_column must be the primary key
|
||||
// columns (in order).
|
||||
// This algorithm performs a full-scan of the table-shard referenced
|
||||
// in 'query' to get "boundary" rows that are num_rows_per_query_part
|
||||
// apart when the table is ordered by the columns listed in
|
||||
// 'split_column'. It then restricts each query-part to the rows
|
||||
// located between two successive boundary rows.
|
||||
// This algorithm supports multiple split_column's of any type,
|
||||
// but is slower than EQUAL_SPLITS.
|
||||
Algorithm query.SplitQueryRequest_Algorithm `protobuf:"varint,7,opt,name=algorithm,enum=query.SplitQueryRequest_Algorithm" json:"algorithm,omitempty"`
|
||||
// Whether to use the new split-query code
|
||||
// that supports multiple split-columns and
|
||||
// the FULL_SCAN algorithm.
|
||||
// This is a temporary field which aids
|
||||
// in the migration of SplitQuery to the new
|
||||
// code.
|
||||
// TODO(erez): Remove this field after the migration
|
||||
// to the SplitQuery version 2.
|
||||
UseSplitQueryV2 bool `protobuf:"varint,8,opt,name=use_split_query_v2,json=useSplitQueryV2" json:"use_split_query_v2,omitempty"`
|
||||
}
|
||||
|
||||
func (m *SplitQueryRequest) Reset() { *m = SplitQueryRequest{} }
|
||||
|
@ -1191,7 +1272,8 @@ func (*SplitQueryResponse_ShardPart) Descriptor() ([]byte, []int) {
|
|||
type SplitQueryResponse_Part struct {
|
||||
// query is the query and bind variables to execute.
|
||||
Query *query.BoundQuery `protobuf:"bytes,1,opt,name=query" json:"query,omitempty"`
|
||||
// key_range_part is set if the query should be executed by ExecuteKeyRanges.
|
||||
// key_range_part is set if the query should be executed by
|
||||
// ExecuteKeyRanges.
|
||||
KeyRangePart *SplitQueryResponse_KeyRangePart `protobuf:"bytes,2,opt,name=key_range_part,json=keyRangePart" json:"key_range_part,omitempty"`
|
||||
// shard_part is set if the query should be executed by ExecuteShards.
|
||||
ShardPart *SplitQueryResponse_ShardPart `protobuf:"bytes,3,opt,name=shard_part,json=shardPart" json:"shard_part,omitempty"`
|
||||
|
@ -1298,82 +1380,88 @@ func init() {
|
|||
}
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 1230 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xd4, 0x59, 0xcf, 0x6f, 0xe3, 0xc4,
|
||||
0x17, 0x97, 0xe3, 0x34, 0x69, 0x9e, 0x93, 0xb4, 0xf5, 0xb7, 0xdd, 0xcd, 0xd7, 0x2c, 0xdb, 0x62,
|
||||
0x81, 0xb6, 0xa0, 0x95, 0xa5, 0xed, 0x8a, 0x1f, 0x42, 0x48, 0x48, 0x0d, 0x15, 0x8a, 0x16, 0x56,
|
||||
0xcb, 0xb4, 0x07, 0x0e, 0x20, 0xcb, 0x4d, 0x46, 0xad, 0x49, 0x62, 0x07, 0xcf, 0x24, 0x22, 0x1c,
|
||||
0xb8, 0x21, 0x71, 0xdb, 0x13, 0x12, 0x42, 0x5c, 0x90, 0xf8, 0x07, 0xf8, 0x13, 0x38, 0x21, 0x71,
|
||||
0xe1, 0xc0, 0x81, 0x1b, 0x7f, 0x02, 0x57, 0x2e, 0x1c, 0x18, 0xcf, 0x8c, 0x7f, 0xc4, 0x4d, 0xd2,
|
||||
0x34, 0x69, 0xa3, 0xf4, 0x54, 0xcf, 0xcc, 0x9b, 0x79, 0xef, 0x7d, 0x3e, 0x9f, 0x79, 0x33, 0xd3,
|
||||
0x40, 0x79, 0x40, 0xcf, 0x1c, 0x8a, 0xad, 0x5e, 0xe0, 0x53, 0x5f, 0x2f, 0x88, 0x96, 0xa1, 0x7d,
|
||||
0xde, 0xc7, 0xc1, 0x50, 0x74, 0x1a, 0x55, 0xea, 0xf7, 0xfc, 0x96, 0x43, 0x1d, 0xd9, 0xd6, 0x06,
|
||||
0x34, 0xe8, 0x35, 0x45, 0xc3, 0xfc, 0x43, 0x81, 0xe2, 0x31, 0x26, 0xc4, 0xf5, 0x3d, 0xfd, 0x15,
|
||||
0xa8, 0xba, 0x9e, 0x4d, 0x03, 0xc7, 0x23, 0x4e, 0x93, 0xb2, 0x9e, 0x9a, 0xb2, 0xa7, 0xec, 0xaf,
|
||||
0xa3, 0x8a, 0xeb, 0x9d, 0x24, 0x9d, 0x7a, 0x1d, 0xaa, 0xe4, 0xdc, 0x09, 0x5a, 0x36, 0x11, 0xf3,
|
||||
0x48, 0x2d, 0xb7, 0xa7, 0xee, 0x6b, 0x07, 0xf7, 0x2c, 0x19, 0x8b, 0x5c, 0xcf, 0x3a, 0x0e, 0xad,
|
||||
0x64, 0x03, 0x55, 0x48, 0xaa, 0x45, 0x8c, 0x4f, 0xa0, 0x9c, 0x1e, 0x66, 0xbe, 0x0b, 0xd4, 0x09,
|
||||
0xce, 0x30, 0xe5, 0x3e, 0xb5, 0x83, 0x8a, 0x25, 0x52, 0x38, 0xe1, 0x9d, 0x48, 0x0e, 0x86, 0x21,
|
||||
0xa6, 0xe2, 0xb3, 0xdd, 0x16, 0xf3, 0xad, 0xec, 0xab, 0xa8, 0x92, 0xea, 0x6d, 0xb4, 0xcc, 0x7f,
|
||||
0x14, 0xa8, 0x1e, 0x7d, 0x81, 0x9b, 0x7d, 0x8a, 0x11, 0x66, 0x0b, 0x11, 0xaa, 0x3f, 0x84, 0x52,
|
||||
0xd3, 0xe9, 0x74, 0x70, 0x10, 0x4e, 0x12, 0x3e, 0x36, 0x2c, 0x81, 0x44, 0x9d, 0xf7, 0x37, 0xde,
|
||||
0x43, 0xeb, 0xc2, 0xa2, 0xd1, 0xd2, 0x5f, 0x85, 0xa2, 0xcc, 0x8e, 0x3b, 0x10, 0xb6, 0xe9, 0xe4,
|
||||
0x50, 0x34, 0xae, 0x3f, 0x80, 0x35, 0x1e, 0x6a, 0x4d, 0xe5, 0x86, 0x5b, 0x32, 0xf0, 0x43, 0xbf,
|
||||
0xef, 0xb5, 0x3e, 0x0a, 0x3f, 0x91, 0x18, 0xd7, 0x5f, 0x07, 0x8d, 0x3a, 0xa7, 0x1d, 0x4c, 0x6d,
|
||||
0x3a, 0xec, 0xe1, 0x5a, 0x9e, 0x99, 0x57, 0x0f, 0xb6, 0xad, 0x98, 0x9d, 0x13, 0x3e, 0x78, 0xc2,
|
||||
0xc6, 0x10, 0xd0, 0xf8, 0x9b, 0x05, 0xae, 0x7b, 0x3e, 0xb5, 0x33, 0xcc, 0xac, 0x71, 0x66, 0x36,
|
||||
0xd9, 0x48, 0x23, 0x4d, 0x8e, 0xf9, 0x5c, 0x81, 0x8d, 0x38, 0x73, 0xd2, 0x63, 0x50, 0x63, 0x06,
|
||||
0xda, 0x1a, 0x0e, 0x02, 0x3f, 0xc8, 0xa4, 0x8d, 0x9e, 0xd5, 0x8f, 0xc2, 0x6e, 0x24, 0x46, 0xaf,
|
||||
0x92, 0xf3, 0x6b, 0x50, 0x08, 0x30, 0xe9, 0x77, 0xa8, 0x4c, 0x5a, 0x97, 0x49, 0x8b, 0x7c, 0xf9,
|
||||
0x08, 0x92, 0x16, 0xe6, 0xcf, 0x39, 0xd8, 0x96, 0x11, 0x71, 0xc6, 0xc9, 0xea, 0x30, 0x62, 0xc0,
|
||||
0x7a, 0x1b, 0x0f, 0x49, 0xcf, 0x69, 0x0a, 0x3a, 0x4a, 0x28, 0x6e, 0xeb, 0x77, 0xa0, 0xc0, 0x15,
|
||||
0x4b, 0x18, 0xd4, 0x2a, 0x1b, 0x91, 0xad, 0x2c, 0x8b, 0x85, 0x85, 0x58, 0x2c, 0x4e, 0x60, 0xf1,
|
||||
0x5b, 0x05, 0x76, 0x32, 0x98, 0xad, 0x04, 0x97, 0xbf, 0xe4, 0xe0, 0xff, 0x32, 0xae, 0x27, 0x12,
|
||||
0xa8, 0xc6, 0x6d, 0x21, 0xf4, 0x25, 0x28, 0x47, 0xdf, 0x2c, 0x3e, 0x41, 0x6b, 0x19, 0x69, 0xed,
|
||||
0x24, 0x8f, 0xe5, 0x70, 0xfb, 0xbd, 0x02, 0xc6, 0x38, 0x0c, 0x57, 0x82, 0xe0, 0xdf, 0x73, 0x70,
|
||||
0x37, 0x09, 0x0e, 0x39, 0xde, 0x19, 0xbe, 0x25, 0xf4, 0x3e, 0x02, 0x60, 0xdf, 0x76, 0xc0, 0x43,
|
||||
0xe6, 0xe4, 0x86, 0x99, 0xc6, 0xd4, 0x45, 0xd9, 0xa0, 0x52, 0x3b, 0xca, 0x6b, 0x39, 0x74, 0x7f,
|
||||
0xa7, 0x40, 0xed, 0x22, 0xa2, 0x2b, 0x41, 0xf6, 0x37, 0xf9, 0x98, 0xec, 0x23, 0x8f, 0xba, 0x74,
|
||||
0x78, 0x6b, 0xf6, 0x32, 0xa3, 0x00, 0xf3, 0x88, 0xed, 0xa6, 0xdf, 0xe9, 0x77, 0x3d, 0xdb, 0x73,
|
||||
0xba, 0x98, 0x9f, 0x89, 0x25, 0xb4, 0x29, 0x46, 0xea, 0x7c, 0xe0, 0x29, 0xeb, 0xd7, 0x3f, 0x86,
|
||||
0xff, 0x49, 0xeb, 0x91, 0x02, 0x50, 0xe0, 0x1a, 0xd9, 0x8f, 0x22, 0x9d, 0x80, 0x84, 0x15, 0x75,
|
||||
0xa0, 0x2d, 0xb1, 0xc8, 0x93, 0xc9, 0x05, 0xa3, 0xb8, 0x90, 0x82, 0xd6, 0xc7, 0x2b, 0xc8, 0x38,
|
||||
0x85, 0xf5, 0x28, 0x06, 0x7d, 0x17, 0xf2, 0xdc, 0x93, 0xc2, 0x3d, 0x69, 0xd1, 0x25, 0x29, 0x74,
|
||||
0xc0, 0x07, 0xf4, 0x6d, 0x58, 0x1b, 0x38, 0x9d, 0x3e, 0xe6, 0x3c, 0x94, 0x91, 0x68, 0xb0, 0x69,
|
||||
0x5a, 0x2a, 0x75, 0x0e, 0x7d, 0x19, 0x41, 0x52, 0xfa, 0xd2, 0x2a, 0x4d, 0x01, 0xb0, 0x12, 0x2a,
|
||||
0xf5, 0x60, 0x83, 0x8b, 0x83, 0x1f, 0x84, 0xdc, 0x20, 0xd1, 0x90, 0x72, 0x05, 0x0d, 0xe5, 0x26,
|
||||
0x1e, 0xf0, 0x6a, 0xfa, 0x80, 0x37, 0xbf, 0x4e, 0xce, 0xb8, 0x43, 0x87, 0x36, 0xcf, 0x97, 0x74,
|
||||
0x69, 0x79, 0x04, 0xc5, 0x30, 0x66, 0x17, 0x8b, 0x78, 0xb4, 0x83, 0xbb, 0x91, 0x69, 0x26, 0x7b,
|
||||
0x14, 0xd9, 0xcd, 0x7b, 0xa1, 0x64, 0x77, 0x68, 0x87, 0x8c, 0xb9, 0x4c, 0x56, 0x1c, 0x92, 0x2e,
|
||||
0x5c, 0x3f, 0x24, 0xe7, 0xd4, 0x08, 0x0e, 0x37, 0x26, 0x8a, 0x87, 0x50, 0x14, 0x94, 0x47, 0x08,
|
||||
0x8c, 0x53, 0x45, 0x64, 0x62, 0x7e, 0x05, 0xdb, 0x1c, 0x98, 0x64, 0x3b, 0x5e, 0xa3, 0x36, 0xb2,
|
||||
0x77, 0x05, 0xf5, 0xc2, 0x5d, 0xc1, 0x7c, 0x9e, 0x83, 0xfb, 0x69, 0x78, 0x96, 0x79, 0x1f, 0x7a,
|
||||
0x23, 0xab, 0x95, 0x7b, 0x23, 0x5a, 0xc9, 0x40, 0xb2, 0x2c, 0xc1, 0xfc, 0xa8, 0xc0, 0xee, 0x44,
|
||||
0x44, 0x56, 0x44, 0x35, 0x3f, 0x29, 0xb0, 0x7d, 0x4c, 0x03, 0xec, 0x74, 0x17, 0x7a, 0x1e, 0xc6,
|
||||
0x22, 0xcb, 0x5d, 0xed, 0xcd, 0xa7, 0xce, 0x86, 0xb8, 0x59, 0x87, 0x9d, 0x4c, 0x94, 0x12, 0xbf,
|
||||
0xa4, 0x70, 0x2a, 0x97, 0x16, 0xce, 0xbf, 0xd8, 0x06, 0x1e, 0x59, 0x65, 0x91, 0x4a, 0x36, 0x73,
|
||||
0xc6, 0xe9, 0x6d, 0xa5, 0x4e, 0x2c, 0xb9, 0xf9, 0x69, 0x6f, 0xaa, 0xb5, 0x19, 0x51, 0x6a, 0xc0,
|
||||
0x0b, 0x63, 0xf3, 0x9b, 0x03, 0xab, 0xbf, 0x99, 0x76, 0x47, 0xd6, 0x5a, 0x78, 0x3b, 0x5f, 0x0b,
|
||||
0x60, 0xd9, 0x3a, 0x94, 0xbf, 0xf4, 0xcd, 0x32, 0x2b, 0x76, 0x4f, 0x61, 0x6f, 0x72, 0xbe, 0x73,
|
||||
0x00, 0xf8, 0xaf, 0x02, 0x2f, 0x66, 0x17, 0x5c, 0xe4, 0xf9, 0x70, 0x2d, 0xf0, 0x8d, 0xbe, 0x09,
|
||||
0xf2, 0x73, 0xbc, 0x09, 0x66, 0x85, 0xf3, 0x03, 0xb8, 0x3f, 0x29, 0xfb, 0x39, 0xc0, 0x7c, 0x07,
|
||||
0xca, 0x87, 0xf8, 0xcc, 0xf5, 0xe6, 0x82, 0xce, 0x7c, 0x1b, 0x2a, 0x72, 0xb6, 0x74, 0x9d, 0xaa,
|
||||
0xa6, 0xca, 0xf4, 0x6a, 0x6a, 0x9e, 0x43, 0xa5, 0xee, 0x77, 0xbb, 0x2e, 0xbd, 0xe9, 0x33, 0xcc,
|
||||
0xdc, 0x84, 0x6a, 0xe4, 0x49, 0x84, 0x69, 0x7e, 0x06, 0x1b, 0xc8, 0xef, 0x74, 0x4e, 0x9d, 0x66,
|
||||
0xfb, 0xc6, 0xbd, 0xeb, 0xb0, 0x99, 0xf8, 0x92, 0xfe, 0x7f, 0x53, 0x60, 0xeb, 0xb8, 0xd7, 0x71,
|
||||
0xa9, 0xa4, 0x64, 0x9e, 0x10, 0xa6, 0x5d, 0x2a, 0x66, 0x7e, 0xf9, 0xb0, 0x5d, 0x4f, 0xc2, 0x38,
|
||||
0xe4, 0xe3, 0x46, 0xbe, 0x7e, 0x34, 0xde, 0x27, 0x9e, 0x35, 0xe1, 0x85, 0x3e, 0x32, 0xe9, 0x7b,
|
||||
0x94, 0xcb, 0x54, 0x45, 0x20, 0x2d, 0x58, 0x8f, 0xf9, 0xab, 0x0a, 0x7a, 0x3a, 0x19, 0x29, 0x85,
|
||||
0x37, 0x59, 0x05, 0x0e, 0x7b, 0x09, 0x4b, 0x25, 0xdc, 0x0d, 0xbb, 0x31, 0x42, 0x17, 0x6c, 0xad,
|
||||
0x67, 0x4e, 0xc0, 0x24, 0x29, 0xcc, 0x8d, 0x4f, 0xa1, 0x1c, 0x69, 0x3a, 0xec, 0x1f, 0x49, 0x54,
|
||||
0x99, 0xba, 0xed, 0x72, 0x33, 0x6c, 0x3b, 0xe3, 0x5d, 0x28, 0xf1, 0xea, 0x7d, 0xe9, 0xda, 0xc9,
|
||||
0x11, 0x92, 0x4b, 0x1f, 0x21, 0xc6, 0x9f, 0x0a, 0xe4, 0xf9, 0xe4, 0x99, 0xef, 0x7f, 0x1f, 0x42,
|
||||
0x35, 0x8e, 0xd2, 0xee, 0xb1, 0xa9, 0x52, 0x34, 0x0f, 0xa6, 0x40, 0x92, 0x86, 0x00, 0x95, 0xdb,
|
||||
0x69, 0x40, 0xea, 0x00, 0xe2, 0xbf, 0xe2, 0x7c, 0x29, 0x41, 0xf1, 0xcb, 0x53, 0x96, 0x8a, 0xd3,
|
||||
0x45, 0x25, 0x12, 0x67, 0xae, 0x43, 0x9e, 0xb8, 0x5f, 0x8a, 0x9b, 0x99, 0x8a, 0xf8, 0xb7, 0xf9,
|
||||
0x18, 0x76, 0xde, 0xc7, 0xf4, 0x38, 0x18, 0x44, 0x25, 0x3a, 0x52, 0xe6, 0x14, 0x98, 0x4c, 0x04,
|
||||
0x77, 0xb2, 0x93, 0xa4, 0x02, 0xde, 0x62, 0xe2, 0x0a, 0x06, 0xf6, 0xc8, 0x4c, 0xed, 0x60, 0x27,
|
||||
0xa1, 0x27, 0x3d, 0x49, 0x23, 0x49, 0xe3, 0xd0, 0x80, 0x5a, 0xd3, 0xef, 0x5a, 0x43, 0xbf, 0x4f,
|
||||
0xfb, 0xa7, 0xd8, 0x1a, 0xb8, 0x94, 0x6d, 0x26, 0xf1, 0x33, 0xc2, 0x69, 0x81, 0xff, 0x79, 0xfc,
|
||||
0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc3, 0xa6, 0x7a, 0xd8, 0x8f, 0x18, 0x00, 0x00,
|
||||
// 1316 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xd4, 0x59, 0x4f, 0x6f, 0xe3, 0x44,
|
||||
0x14, 0x97, 0xe3, 0x34, 0x69, 0x9e, 0x93, 0xb4, 0xeb, 0x6d, 0x77, 0x43, 0x58, 0xb6, 0xc5, 0x02,
|
||||
0x6d, 0x81, 0x95, 0xa5, 0xcd, 0xf2, 0x4f, 0x08, 0x09, 0x68, 0xa8, 0x50, 0xb4, 0xb0, 0x2a, 0xd3,
|
||||
0x0a, 0x71, 0x00, 0x59, 0x6e, 0x32, 0x6a, 0x4d, 0x13, 0x3b, 0x78, 0xc6, 0x81, 0x72, 0xe0, 0x86,
|
||||
0xc4, 0x6d, 0x0f, 0x08, 0x09, 0x21, 0x2e, 0x7c, 0x04, 0xbe, 0x01, 0x9c, 0x38, 0x72, 0xe0, 0xc0,
|
||||
0x8d, 0x8f, 0x00, 0x5f, 0x80, 0x03, 0xe3, 0x99, 0xf1, 0x9f, 0xb8, 0x49, 0x9a, 0x26, 0x6d, 0x95,
|
||||
0x9e, 0xea, 0x99, 0xf7, 0x66, 0xde, 0x7b, 0xbf, 0xdf, 0x9b, 0x37, 0xf3, 0x1a, 0x28, 0x0f, 0xe8,
|
||||
0xa1, 0x4d, 0xb1, 0xd9, 0xf7, 0x3d, 0xea, 0xe9, 0x05, 0x31, 0xaa, 0x6b, 0x9f, 0x07, 0xd8, 0x3f,
|
||||
0x11, 0x93, 0xf5, 0x2a, 0xf5, 0xfa, 0x5e, 0xc7, 0xa6, 0xb6, 0x1c, 0x6b, 0x03, 0xea, 0xf7, 0xdb,
|
||||
0x62, 0x60, 0xfc, 0xa9, 0x40, 0x71, 0x0f, 0x13, 0xe2, 0x78, 0xae, 0xfe, 0x3c, 0x54, 0x1d, 0xd7,
|
||||
0xa2, 0xbe, 0xed, 0x12, 0xbb, 0x4d, 0xd9, 0x4c, 0x4d, 0xd9, 0x54, 0xb6, 0x96, 0x51, 0xc5, 0x71,
|
||||
0xf7, 0x93, 0x49, 0xbd, 0x09, 0x55, 0x72, 0x64, 0xfb, 0x1d, 0x8b, 0x88, 0x75, 0xa4, 0x96, 0xdb,
|
||||
0x54, 0xb7, 0xb4, 0xc6, 0x1d, 0x53, 0xfa, 0x22, 0xf7, 0x33, 0xf7, 0x42, 0x2d, 0x39, 0x40, 0x15,
|
||||
0x92, 0x1a, 0x91, 0xfa, 0x27, 0x50, 0x4e, 0x8b, 0x99, 0xed, 0x02, 0xb5, 0xfd, 0x43, 0x4c, 0xb9,
|
||||
0x4d, 0xad, 0x51, 0x31, 0x45, 0x08, 0xfb, 0x7c, 0x12, 0x49, 0x61, 0xe8, 0x62, 0xca, 0x3f, 0xcb,
|
||||
0xe9, 0x30, 0xdb, 0xca, 0x96, 0x8a, 0x2a, 0xa9, 0xd9, 0x56, 0xc7, 0xf8, 0x2e, 0x07, 0xd5, 0x9d,
|
||||
0x2f, 0x71, 0x3b, 0xa0, 0x18, 0x61, 0xb6, 0x11, 0xa1, 0xfa, 0x7d, 0x28, 0xb5, 0xed, 0x6e, 0x17,
|
||||
0xfb, 0xe1, 0x22, 0x61, 0x63, 0xc5, 0x14, 0x48, 0x34, 0xf9, 0x7c, 0xeb, 0x5d, 0xb4, 0x2c, 0x34,
|
||||
0x5a, 0x1d, 0xfd, 0x05, 0x28, 0xca, 0xe8, 0xb8, 0x01, 0xa1, 0x9b, 0x0e, 0x0e, 0x45, 0x72, 0xfd,
|
||||
0x1e, 0x2c, 0x71, 0x57, 0x6b, 0x2a, 0x57, 0xbc, 0x21, 0x1d, 0xdf, 0xf6, 0x02, 0xb7, 0xf3, 0x61,
|
||||
0xf8, 0x89, 0x84, 0x5c, 0x7f, 0x05, 0x34, 0x6a, 0x1f, 0x74, 0x31, 0xb5, 0xe8, 0x49, 0x1f, 0xd7,
|
||||
0xf2, 0x4c, 0xbd, 0xda, 0x58, 0x33, 0x63, 0x76, 0xf6, 0xb9, 0x70, 0x9f, 0xc9, 0x10, 0xd0, 0xf8,
|
||||
0x9b, 0x39, 0xae, 0xbb, 0x1e, 0xb5, 0x32, 0xcc, 0x2c, 0x71, 0x66, 0x56, 0x99, 0xa4, 0x35, 0x44,
|
||||
0x4e, 0x1d, 0x96, 0x8f, 0xf1, 0x09, 0xe9, 0xdb, 0x6d, 0x5c, 0x2b, 0x30, 0x9d, 0x12, 0x8a, 0xc7,
|
||||
0xc6, 0x13, 0x05, 0x56, 0x62, 0x54, 0x48, 0x9f, 0xd1, 0x80, 0x19, 0xa0, 0x4b, 0xd8, 0xf7, 0x3d,
|
||||
0x3f, 0x03, 0x09, 0xda, 0x6d, 0xee, 0x84, 0xd3, 0x48, 0x48, 0xcf, 0x83, 0xc7, 0x8b, 0x50, 0xf0,
|
||||
0x31, 0x09, 0xba, 0x54, 0x02, 0xa2, 0x4b, 0x40, 0x04, 0x16, 0x5c, 0x82, 0xa4, 0x86, 0xf1, 0x4b,
|
||||
0x0e, 0xd6, 0xa4, 0x47, 0x3c, 0x1b, 0xc8, 0xe2, 0xb0, 0x95, 0x06, 0x32, 0x3f, 0x0c, 0xa4, 0x7e,
|
||||
0x0b, 0x0a, 0x3c, 0x9b, 0x09, 0xa3, 0x41, 0x65, 0x12, 0x39, 0xca, 0x32, 0x5c, 0x98, 0x8b, 0xe1,
|
||||
0xe2, 0x68, 0x86, 0x8d, 0xef, 0x15, 0x58, 0xcf, 0x60, 0xb6, 0x10, 0x5c, 0xfe, 0x96, 0x83, 0xa7,
|
||||
0xa4, 0x5f, 0x8f, 0x24, 0x50, 0xad, 0xeb, 0x42, 0xe8, 0xb3, 0x50, 0x8e, 0xbe, 0x99, 0x7f, 0x82,
|
||||
0xd6, 0x32, 0xd2, 0x8e, 0x93, 0x38, 0xae, 0x86, 0xdb, 0x1f, 0x15, 0xa8, 0x8f, 0xc2, 0x70, 0x21,
|
||||
0x08, 0xfe, 0x23, 0x07, 0xb7, 0x13, 0xe7, 0x90, 0xed, 0x1e, 0xe2, 0x6b, 0x42, 0xef, 0x03, 0x00,
|
||||
0xf6, 0x6d, 0xf9, 0xdc, 0x65, 0x4e, 0x6e, 0x18, 0x69, 0x4c, 0x5d, 0x14, 0x0d, 0x2a, 0x1d, 0x47,
|
||||
0x71, 0x5d, 0x0d, 0xdd, 0x3f, 0x28, 0x50, 0x3b, 0x8d, 0xe8, 0x42, 0x90, 0xfd, 0x6d, 0x3e, 0x26,
|
||||
0x7b, 0xc7, 0xa5, 0x0e, 0x3d, 0xb9, 0x36, 0x67, 0x99, 0x51, 0x80, 0xb9, 0xc7, 0x56, 0xdb, 0xeb,
|
||||
0x06, 0x3d, 0xd7, 0x72, 0xed, 0x1e, 0xe6, 0xf7, 0x65, 0x09, 0xad, 0x0a, 0x49, 0x93, 0x0b, 0x1e,
|
||||
0xb3, 0x79, 0xfd, 0x63, 0xb8, 0x29, 0xb5, 0x87, 0x0a, 0x40, 0x81, 0xe7, 0xc8, 0x56, 0xe4, 0xe9,
|
||||
0x18, 0x24, 0xcc, 0x68, 0x02, 0xdd, 0x10, 0x9b, 0x3c, 0x1a, 0x5f, 0x30, 0x8a, 0x73, 0x65, 0xd0,
|
||||
0xf2, 0xe8, 0x0c, 0xaa, 0x1f, 0xc0, 0x72, 0xe4, 0x83, 0xbe, 0x01, 0x79, 0x6e, 0x49, 0xe1, 0x96,
|
||||
0xb4, 0xe8, 0x01, 0x15, 0x1a, 0xe0, 0x02, 0x7d, 0x0d, 0x96, 0x06, 0x76, 0x37, 0xc0, 0x9c, 0x87,
|
||||
0x32, 0x12, 0x03, 0xb6, 0x4c, 0x4b, 0x85, 0xce, 0xa1, 0x2f, 0x23, 0x48, 0x4a, 0x5f, 0x3a, 0x4b,
|
||||
0x53, 0x00, 0x2c, 0x44, 0x96, 0xba, 0xb0, 0xc2, 0x93, 0x83, 0x5f, 0x84, 0x5c, 0x21, 0xc9, 0x21,
|
||||
0xe5, 0x1c, 0x39, 0x94, 0x1b, 0x7b, 0xc1, 0xab, 0xe9, 0x0b, 0xde, 0xf8, 0x26, 0xb9, 0xe3, 0xb6,
|
||||
0x6d, 0xda, 0x3e, 0xba, 0xa2, 0x47, 0xcb, 0x03, 0x28, 0x86, 0x3e, 0x3b, 0x58, 0xf8, 0xa3, 0x35,
|
||||
0x6e, 0x47, 0xaa, 0x99, 0xe8, 0x51, 0xa4, 0x37, 0xeb, 0x63, 0x93, 0xbd, 0xaf, 0x6d, 0x32, 0xe2,
|
||||
0xa1, 0x59, 0xb1, 0x49, 0xba, 0x70, 0xfd, 0x94, 0xdc, 0x53, 0x43, 0x38, 0x5c, 0x5a, 0x52, 0xdc,
|
||||
0x87, 0xa2, 0xa0, 0x3c, 0x42, 0x60, 0x54, 0x56, 0x44, 0x2a, 0xc6, 0xd7, 0xb0, 0xc6, 0x81, 0x49,
|
||||
0x8e, 0xe3, 0x05, 0xe6, 0x46, 0xf6, 0xad, 0xa0, 0x9e, 0x7a, 0x2b, 0x18, 0x4f, 0x72, 0x70, 0x37,
|
||||
0x0d, 0xcf, 0x55, 0xbe, 0x87, 0x5e, 0xcd, 0xe6, 0xca, 0x9d, 0xa1, 0x5c, 0xc9, 0x40, 0x72, 0x55,
|
||||
0x09, 0xf3, 0xb3, 0x02, 0x1b, 0x63, 0x11, 0x59, 0x90, 0xac, 0xf9, 0x55, 0x81, 0xb5, 0x3d, 0xea,
|
||||
0x63, 0xbb, 0x37, 0x57, 0xeb, 0x18, 0x27, 0x59, 0xee, 0x7c, 0xfd, 0xa0, 0x3a, 0x25, 0xe2, 0x13,
|
||||
0xee, 0x3e, 0xa3, 0x09, 0xeb, 0x99, 0x08, 0x24, 0xb6, 0x49, 0x51, 0x55, 0xce, 0x2c, 0xaa, 0x7f,
|
||||
0xb3, 0xc3, 0x3d, 0xb4, 0xcb, 0x3c, 0x55, 0x6e, 0x6a, 0x34, 0xd2, 0x61, 0xa9, 0x63, 0xcb, 0x71,
|
||||
0x7e, 0x52, 0xbf, 0xb5, 0x34, 0x1d, 0x82, 0x46, 0x0b, 0x9e, 0x1e, 0x19, 0xdf, 0x0c, 0x58, 0xfd,
|
||||
0xc3, 0xf2, 0x7a, 0x68, 0xaf, 0xb9, 0x8f, 0xfa, 0x85, 0x00, 0x96, 0xad, 0x51, 0xf9, 0x33, 0xfb,
|
||||
0x99, 0x69, 0xb1, 0x7b, 0x0c, 0x9b, 0xe3, 0xe3, 0x9d, 0x01, 0xc0, 0xff, 0x14, 0x78, 0x26, 0xbb,
|
||||
0xe1, 0x3c, 0xad, 0xc5, 0x85, 0xc0, 0x37, 0xdc, 0x2f, 0xe4, 0x67, 0xe8, 0x17, 0xa6, 0x85, 0xf3,
|
||||
0x7d, 0xb8, 0x3b, 0x2e, 0xfa, 0x19, 0xc0, 0x7c, 0x13, 0xca, 0xdb, 0xf8, 0xd0, 0x71, 0x67, 0x82,
|
||||
0xce, 0x78, 0x03, 0x2a, 0x72, 0xb5, 0x34, 0x9d, 0xaa, 0xb4, 0xca, 0xe4, 0x4a, 0x6b, 0x1c, 0x41,
|
||||
0xa5, 0xe9, 0xf5, 0x7a, 0x0e, 0xbd, 0xec, 0xfb, 0xcd, 0x58, 0x85, 0x6a, 0x64, 0x49, 0xb8, 0x69,
|
||||
0x7c, 0x06, 0x2b, 0xc8, 0xeb, 0x76, 0x0f, 0xec, 0xf6, 0xf1, 0xa5, 0x5b, 0xd7, 0x61, 0x35, 0xb1,
|
||||
0x25, 0xed, 0xff, 0x9b, 0x83, 0x1b, 0x7b, 0xfd, 0xae, 0x43, 0x25, 0x25, 0xb3, 0xb8, 0x30, 0xe9,
|
||||
0xc1, 0x31, 0x75, 0x57, 0xc4, 0x4e, 0x3d, 0x09, 0xfd, 0x90, 0x8d, 0x8f, 0x2c, 0x96, 0x1a, 0x9f,
|
||||
0x13, 0x2d, 0x4f, 0xf8, 0xd8, 0x8f, 0x54, 0x02, 0x97, 0xf2, 0x34, 0x55, 0x11, 0x48, 0x0d, 0x36,
|
||||
0xa3, 0xbf, 0x0c, 0xb7, 0xdd, 0xa0, 0x67, 0xf9, 0xde, 0x17, 0xc4, 0xea, 0x33, 0xe7, 0xf9, 0xce,
|
||||
0x56, 0xdf, 0xf6, 0x29, 0xef, 0x81, 0x55, 0x74, 0x93, 0x89, 0x11, 0x93, 0xee, 0x62, 0x9f, 0x1b,
|
||||
0xdf, 0x65, 0x22, 0xfd, 0x6d, 0x28, 0xd9, 0xdd, 0x43, 0xcf, 0x77, 0xe8, 0x51, 0x4f, 0x76, 0x3a,
|
||||
0x86, 0x74, 0xf3, 0x14, 0x32, 0xe6, 0x3b, 0x91, 0x26, 0x4a, 0x16, 0xe9, 0x2f, 0x81, 0x1e, 0x10,
|
||||
0x6c, 0x09, 0xe7, 0x84, 0xd1, 0x41, 0x43, 0xb6, 0x3d, 0x2b, 0x4c, 0x92, 0x6c, 0xf3, 0x51, 0xc3,
|
||||
0xf8, 0x5d, 0x05, 0x3d, 0xbd, 0xaf, 0xcc, 0xd7, 0xd7, 0xd8, 0x35, 0x11, 0xce, 0x12, 0x86, 0x77,
|
||||
0x78, 0x64, 0x37, 0x62, 0x1a, 0x4f, 0xe9, 0x9a, 0xa1, 0xdb, 0x48, 0xaa, 0xd7, 0x3f, 0x85, 0x72,
|
||||
0x74, 0xf0, 0x78, 0x38, 0x69, 0x36, 0x94, 0x89, 0xb5, 0x21, 0x37, 0x45, 0x6d, 0xa8, 0xbf, 0x05,
|
||||
0x25, 0x7e, 0xc5, 0x9c, 0xb9, 0x77, 0x72, 0xcf, 0xe5, 0xd2, 0xf7, 0x5c, 0xfd, 0x2f, 0x05, 0xf2,
|
||||
0x7c, 0xf1, 0xd4, 0x0f, 0xd8, 0x0f, 0xa0, 0x1a, 0x7b, 0x29, 0xd8, 0x13, 0x99, 0x7d, 0x6f, 0x02,
|
||||
0x24, 0x69, 0x08, 0x50, 0xf9, 0x38, 0x0d, 0x48, 0x13, 0x40, 0xfc, 0xcb, 0x9f, 0x6f, 0x25, 0xf2,
|
||||
0xf0, 0xb9, 0x09, 0x5b, 0xc5, 0xe1, 0xa2, 0x12, 0x89, 0x23, 0xd7, 0x21, 0x4f, 0x9c, 0xaf, 0xc4,
|
||||
0xa3, 0x45, 0x45, 0xfc, 0xdb, 0x78, 0x08, 0xeb, 0xef, 0x61, 0xba, 0xe7, 0x0f, 0xa2, 0x7b, 0x24,
|
||||
0x3a, 0x3e, 0x13, 0x60, 0x32, 0x10, 0xdc, 0xca, 0x2e, 0x92, 0x19, 0xf0, 0x3a, 0x3b, 0x01, 0xfe,
|
||||
0xc0, 0x1a, 0x5a, 0xa9, 0x35, 0xd6, 0x13, 0x7a, 0xd2, 0x8b, 0x34, 0x92, 0x0c, 0xb6, 0xeb, 0x50,
|
||||
0x6b, 0x7b, 0x3d, 0xf3, 0xc4, 0x0b, 0x68, 0x70, 0x80, 0xcd, 0x81, 0x43, 0xd9, 0x89, 0x17, 0xbf,
|
||||
0x91, 0x1c, 0x14, 0xf8, 0x9f, 0x87, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0x12, 0x77, 0x8b, 0xc2,
|
||||
0x6c, 0x19, 0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -202,6 +202,17 @@ func (ftc *fakeTabletConn) SplitQuery(ctx context.Context, query querytypes.Boun
|
|||
return nil, fmt.Errorf("not implemented in this test")
|
||||
}
|
||||
|
||||
// SplitQuery is part of the TabletConn interface
|
||||
func (ftc *fakeTabletConn) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
query querytypes.BoundQuery,
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) ([]querytypes.QuerySplit, error) {
|
||||
return nil, fmt.Errorf("not implemented in this test")
|
||||
}
|
||||
|
||||
type streamHealthReader struct {
|
||||
c <-chan *querypb.StreamHealthResponse
|
||||
err *error
|
||||
|
|
|
@ -152,11 +152,24 @@ func (q *query) SplitQuery(ctx context.Context, request *querypb.SplitQueryReque
|
|||
request.EffectiveCallerId,
|
||||
request.ImmediateCallerId,
|
||||
)
|
||||
|
||||
bq, err := querytypes.Proto3ToBoundQuery(request.Query)
|
||||
if err != nil {
|
||||
return nil, tabletserver.ToGRPCError(err)
|
||||
}
|
||||
splits, err := q.server.SplitQuery(ctx, request.Target, bq.Sql, bq.BindVariables, request.SplitColumn, request.SplitCount, request.SessionId)
|
||||
splits := []querytypes.QuerySplit{}
|
||||
splits, err = queryservice.CallCorrectSplitQuery(
|
||||
q.server,
|
||||
request.UseSplitQueryV2,
|
||||
ctx,
|
||||
request.Target,
|
||||
bq.Sql,
|
||||
bq.BindVariables,
|
||||
request.SplitColumn,
|
||||
request.SplitCount,
|
||||
request.NumRowsPerQueryPart,
|
||||
request.Algorithm,
|
||||
request.SessionId)
|
||||
if err != nil {
|
||||
return nil, tabletserver.ToGRPCError(err)
|
||||
}
|
||||
|
|
|
@ -251,6 +251,8 @@ func (conn *gRPCQueryClient) Rollback(ctx context.Context, transactionID int64)
|
|||
}
|
||||
|
||||
// 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, query querytypes.BoundQuery, splitColumn string, splitCount int64) (queries []querytypes.QuerySplit, err error) {
|
||||
conn.mu.RLock()
|
||||
defer conn.mu.RUnlock()
|
||||
|
@ -264,13 +266,59 @@ func (conn *gRPCQueryClient) SplitQuery(ctx context.Context, query querytypes.Bo
|
|||
return nil, tabletconn.TabletErrorFromGRPC(err)
|
||||
}
|
||||
req := &querypb.SplitQueryRequest{
|
||||
Target: conn.target,
|
||||
EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx),
|
||||
ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx),
|
||||
Query: q,
|
||||
SplitColumn: splitColumn,
|
||||
SplitCount: splitCount,
|
||||
SessionId: conn.sessionID,
|
||||
Target: conn.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,
|
||||
SessionId: conn.sessionID,
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// SplitQueryV2 is the stub for TabletServer.SplitQuery RPC
|
||||
func (conn *gRPCQueryClient) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
query querytypes.BoundQuery,
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) (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: conn.target,
|
||||
EffectiveCallerId: callerid.EffectiveCallerIDFromContext(ctx),
|
||||
ImmediateCallerId: callerid.ImmediateCallerIDFromContext(ctx),
|
||||
Query: q,
|
||||
SplitColumn: splitColumns,
|
||||
SplitCount: splitCount,
|
||||
NumRowsPerQueryPart: numRowsPerQueryPart,
|
||||
Algorithm: algorithm,
|
||||
UseSplitQueryV2: true,
|
||||
SessionId: conn.sessionID,
|
||||
}
|
||||
sqr, err := conn.c.SplitQuery(ctx, req)
|
||||
if err != nil {
|
||||
|
|
|
@ -288,7 +288,7 @@ func (qre *QueryExecutor) checkPermissions() 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)
|
||||
qre.qe.accessCheckerLogger.Infof("%s", errStr)
|
||||
return NewTabletError(ErrFail, vtrpcpb.ErrorCode_PERMISSION_DENIED, "%s", errStr)
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -41,8 +41,24 @@ type QueryService interface {
|
|||
ExecuteBatch(ctx context.Context, target *querypb.Target, queries []querytypes.BoundQuery, sessionID int64, asTransaction bool, transactionID int64) ([]sqltypes.Result, error)
|
||||
|
||||
// SplitQuery is a map reduce helper function
|
||||
// TODO(erez): Remove this and rename the following func to SplitQuery
|
||||
// once we migrate to SplitQuery V2.
|
||||
SplitQuery(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}, splitColumn string, splitCount int64, sessionID int64) ([]querytypes.QuerySplit, error)
|
||||
|
||||
// SplitQueryV2 is a MapReduce helper function.
|
||||
// This is version of SplitQuery supports multiple algorithms and multiple split columns.
|
||||
// See the documentation of SplitQueryRequest in 'proto/vtgate.proto' for more information.
|
||||
SplitQueryV2(
|
||||
ctx context.Context,
|
||||
target *querypb.Target,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm,
|
||||
sessionID int64) ([]querytypes.QuerySplit, error)
|
||||
|
||||
// StreamHealthRegister registers a listener for StreamHealth
|
||||
StreamHealthRegister(chan<- *querypb.StreamHealthResponse) (int, error)
|
||||
|
||||
|
@ -96,10 +112,25 @@ func (e *ErrorQueryService) ExecuteBatch(ctx context.Context, target *querypb.Ta
|
|||
}
|
||||
|
||||
// SplitQuery is part of QueryService interface
|
||||
// TODO(erez): Remove once the migration to SplitQuery V2 is done.
|
||||
func (e *ErrorQueryService) SplitQuery(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}, splitColumn string, splitCount int64, sessionID int64) ([]querytypes.QuerySplit, error) {
|
||||
return nil, fmt.Errorf("ErrorQueryService does not implement any method")
|
||||
}
|
||||
|
||||
// SplitQuery is part of QueryService interface
|
||||
func (e *ErrorQueryService) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
target *querypb.Target,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm,
|
||||
sessionID int64) ([]querytypes.QuerySplit, error) {
|
||||
return nil, fmt.Errorf("ErrorQueryService does not implement any method")
|
||||
}
|
||||
|
||||
// StreamHealthRegister is part of QueryService interface
|
||||
func (e *ErrorQueryService) StreamHealthRegister(chan<- *querypb.StreamHealthResponse) (int, error) {
|
||||
return 0, fmt.Errorf("ErrorQueryService does not implement any method")
|
||||
|
@ -116,3 +147,59 @@ func (e *ErrorQueryService) HandlePanic(*error) {
|
|||
|
||||
// make sure ErrorQueryService implements QueryService
|
||||
var _ QueryService = &ErrorQueryService{}
|
||||
|
||||
// CallCorrectSplitQuery calls the correct SplitQuery.
|
||||
// This trivial logic is encapsulated in a function here so it can be easily tested.
|
||||
// TODO(erez): Remove once the migration to SplitQueryV2 is done.
|
||||
func CallCorrectSplitQuery(
|
||||
queryService QueryService,
|
||||
useSplitQueryV2 bool,
|
||||
ctx context.Context,
|
||||
target *querypb.Target,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm,
|
||||
sessionID int64) ([]querytypes.QuerySplit, error) {
|
||||
|
||||
if useSplitQueryV2 {
|
||||
return queryService.SplitQueryV2(
|
||||
ctx,
|
||||
target,
|
||||
sql,
|
||||
bindVariables,
|
||||
splitColumns,
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm,
|
||||
sessionID)
|
||||
}
|
||||
return queryService.SplitQuery(
|
||||
ctx,
|
||||
target,
|
||||
sql,
|
||||
bindVariables,
|
||||
splitColumnsToSplitColumn(splitColumns),
|
||||
splitCount,
|
||||
sessionID)
|
||||
}
|
||||
|
||||
// SplitColumnsToSplitColumn returns the first SplitColumn in the given slice or an empty
|
||||
// string if the slice is empty.
|
||||
//
|
||||
// This method is used to get the traditional behavior when accessing the SplitColumn field in an
|
||||
// older SplitQuery-V1 querypb.SplitQueryRequest represented in the newer SplitQuery-V2
|
||||
// querypb.SplitQueryRequest message. In the new V2 message the SplitColumn field has been converted
|
||||
// into a repeated string field.
|
||||
// TODO(erez): Remove this function when migration to SplitQueryV2 is done.
|
||||
func splitColumnsToSplitColumn(splitColumns []string) string {
|
||||
if len(splitColumns) == 0 {
|
||||
return ""
|
||||
}
|
||||
return splitColumns[0]
|
||||
}
|
||||
|
||||
// Command to generate a mock for this interface with mockgen.
|
||||
//go:generate mockgen -source $GOFILE -destination queryservice_testing/mock_queryservice.go -package queryservice_testing
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
package queryservice
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
querypb "github.com/youtube/vitess/go/vt/proto/query"
|
||||
"github.com/youtube/vitess/go/vt/tabletserver/queryservice/queryservice_testing"
|
||||
)
|
||||
|
||||
var (
|
||||
target querypb.Target
|
||||
sql = "It's an SQL statement!"
|
||||
bindVariables = map[string]interface{}{}
|
||||
splitCount int64 = 123
|
||||
numRowsPerQueryPart int64 = 456
|
||||
sessionID int64 = 789
|
||||
algorithm = querypb.SplitQueryRequest_EQUAL_SPLITS
|
||||
)
|
||||
|
||||
func TestCallCorrectSplitQueryCallV1NoSplitColumn(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
mockQueryService := queryservice_testing.NewMockQueryService(mockCtrl)
|
||||
|
||||
mockQueryService.EXPECT().SplitQuery(
|
||||
context.Background(),
|
||||
&target,
|
||||
sql,
|
||||
bindVariables,
|
||||
"",
|
||||
splitCount,
|
||||
sessionID)
|
||||
CallCorrectSplitQuery(
|
||||
mockQueryService,
|
||||
false, /* useSplitQueryV2 */
|
||||
context.Background(),
|
||||
&target,
|
||||
sql,
|
||||
bindVariables,
|
||||
[]string{}, /* SplitColumns */
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm,
|
||||
sessionID)
|
||||
}
|
||||
|
||||
func TestCallCorrectSplitQueryCallV1WithSplitColumn(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
mockQueryService := queryservice_testing.NewMockQueryService(mockCtrl)
|
||||
|
||||
mockQueryService.EXPECT().SplitQuery(
|
||||
context.Background(),
|
||||
&target,
|
||||
sql,
|
||||
bindVariables,
|
||||
"First Split Column",
|
||||
splitCount,
|
||||
sessionID)
|
||||
CallCorrectSplitQuery(
|
||||
mockQueryService,
|
||||
false, /* useSplitQueryV2 */
|
||||
context.Background(),
|
||||
&target,
|
||||
sql,
|
||||
bindVariables,
|
||||
[]string{"First Split Column"}, /* SplitColumns */
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm,
|
||||
sessionID)
|
||||
}
|
||||
|
||||
func TestCallCorrectSplitQueryCallV2(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
mockQueryService := queryservice_testing.NewMockQueryService(mockCtrl)
|
||||
splitColumns := []string{"col1", "col2"}
|
||||
mockQueryService.EXPECT().SplitQueryV2(
|
||||
context.Background(),
|
||||
&target,
|
||||
sql,
|
||||
bindVariables,
|
||||
splitColumns,
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm,
|
||||
sessionID)
|
||||
CallCorrectSplitQuery(
|
||||
mockQueryService,
|
||||
true, /* useSplitQueryV2 */
|
||||
context.Background(),
|
||||
&target,
|
||||
sql,
|
||||
bindVariables,
|
||||
splitColumns,
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm,
|
||||
sessionID)
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
// Automatically generated by MockGen. DO NOT EDIT!
|
||||
// Source: queryservice.go
|
||||
|
||||
package queryservice_testing
|
||||
|
||||
import (
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
sqltypes "github.com/youtube/vitess/go/sqltypes"
|
||||
query "github.com/youtube/vitess/go/vt/proto/query"
|
||||
querytypes "github.com/youtube/vitess/go/vt/tabletserver/querytypes"
|
||||
context "golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Mock of QueryService interface
|
||||
type MockQueryService struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *_MockQueryServiceRecorder
|
||||
}
|
||||
|
||||
// Recorder for MockQueryService (not exported)
|
||||
type _MockQueryServiceRecorder struct {
|
||||
mock *MockQueryService
|
||||
}
|
||||
|
||||
func NewMockQueryService(ctrl *gomock.Controller) *MockQueryService {
|
||||
mock := &MockQueryService{ctrl: ctrl}
|
||||
mock.recorder = &_MockQueryServiceRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
func (_m *MockQueryService) EXPECT() *_MockQueryServiceRecorder {
|
||||
return _m.recorder
|
||||
}
|
||||
|
||||
func (_m *MockQueryService) GetSessionId(keyspace string, shard string) (int64, error) {
|
||||
ret := _m.ctrl.Call(_m, "GetSessionId", keyspace, shard)
|
||||
ret0, _ := ret[0].(int64)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockQueryServiceRecorder) GetSessionId(arg0, arg1 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "GetSessionId", arg0, arg1)
|
||||
}
|
||||
|
||||
func (_m *MockQueryService) Begin(ctx context.Context, target *query.Target, sessionID int64) (int64, error) {
|
||||
ret := _m.ctrl.Call(_m, "Begin", ctx, target, sessionID)
|
||||
ret0, _ := ret[0].(int64)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockQueryServiceRecorder) Begin(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Begin", arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
func (_m *MockQueryService) Commit(ctx context.Context, target *query.Target, sessionID int64, transactionID int64) error {
|
||||
ret := _m.ctrl.Call(_m, "Commit", ctx, target, sessionID, transactionID)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockQueryServiceRecorder) Commit(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Commit", arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
func (_m *MockQueryService) Rollback(ctx context.Context, target *query.Target, sessionID int64, transactionID int64) error {
|
||||
ret := _m.ctrl.Call(_m, "Rollback", ctx, target, sessionID, transactionID)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockQueryServiceRecorder) Rollback(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Rollback", arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
func (_m *MockQueryService) Execute(ctx context.Context, target *query.Target, sql string, bindVariables map[string]interface{}, sessionID int64, transactionID int64) (*sqltypes.Result, error) {
|
||||
ret := _m.ctrl.Call(_m, "Execute", ctx, target, sql, bindVariables, sessionID, transactionID)
|
||||
ret0, _ := ret[0].(*sqltypes.Result)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockQueryServiceRecorder) Execute(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Execute", arg0, arg1, arg2, arg3, arg4, arg5)
|
||||
}
|
||||
|
||||
func (_m *MockQueryService) StreamExecute(ctx context.Context, target *query.Target, sql string, bindVariables map[string]interface{}, sessionID int64, sendReply func(*sqltypes.Result) error) error {
|
||||
ret := _m.ctrl.Call(_m, "StreamExecute", ctx, target, sql, bindVariables, sessionID, sendReply)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockQueryServiceRecorder) StreamExecute(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "StreamExecute", arg0, arg1, arg2, arg3, arg4, arg5)
|
||||
}
|
||||
|
||||
func (_m *MockQueryService) ExecuteBatch(ctx context.Context, target *query.Target, queries []querytypes.BoundQuery, sessionID int64, asTransaction bool, transactionID int64) ([]sqltypes.Result, error) {
|
||||
ret := _m.ctrl.Call(_m, "ExecuteBatch", ctx, target, queries, sessionID, asTransaction, transactionID)
|
||||
ret0, _ := ret[0].([]sqltypes.Result)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockQueryServiceRecorder) ExecuteBatch(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "ExecuteBatch", arg0, arg1, arg2, arg3, arg4, arg5)
|
||||
}
|
||||
|
||||
func (_m *MockQueryService) SplitQuery(ctx context.Context, target *query.Target, sql string, bindVariables map[string]interface{}, splitColumn string, splitCount int64, sessionID int64) ([]querytypes.QuerySplit, error) {
|
||||
ret := _m.ctrl.Call(_m, "SplitQuery", ctx, target, sql, bindVariables, splitColumn, splitCount, sessionID)
|
||||
ret0, _ := ret[0].([]querytypes.QuerySplit)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockQueryServiceRecorder) SplitQuery(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "SplitQuery", arg0, arg1, arg2, arg3, arg4, arg5, arg6)
|
||||
}
|
||||
|
||||
func (_m *MockQueryService) SplitQueryV2(ctx context.Context, target *query.Target, sql string, bindVariables map[string]interface{}, splitColumns []string, splitCount int64, numRowsPerQueryPart int64, algorithm query.SplitQueryRequest_Algorithm, sessionID int64) ([]querytypes.QuerySplit, error) {
|
||||
ret := _m.ctrl.Call(_m, "SplitQueryV2", ctx, target, sql, bindVariables, splitColumns, splitCount, numRowsPerQueryPart, algorithm, sessionID)
|
||||
ret0, _ := ret[0].([]querytypes.QuerySplit)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockQueryServiceRecorder) SplitQueryV2(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "SplitQueryV2", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
|
||||
}
|
||||
|
||||
func (_m *MockQueryService) StreamHealthRegister(_param0 chan<- *query.StreamHealthResponse) (int, error) {
|
||||
ret := _m.ctrl.Call(_m, "StreamHealthRegister", _param0)
|
||||
ret0, _ := ret[0].(int)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockQueryServiceRecorder) StreamHealthRegister(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "StreamHealthRegister", arg0)
|
||||
}
|
||||
|
||||
func (_m *MockQueryService) StreamHealthUnregister(_param0 int) error {
|
||||
ret := _m.ctrl.Call(_m, "StreamHealthUnregister", _param0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockQueryServiceRecorder) StreamHealthUnregister(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "StreamHealthUnregister", arg0)
|
||||
}
|
||||
|
||||
func (_m *MockQueryService) HandlePanic(_param0 *error) {
|
||||
_m.ctrl.Call(_m, "HandlePanic", _param0)
|
||||
}
|
||||
|
||||
func (_mr *_MockQueryServiceRecorder) HandlePanic(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "HandlePanic", arg0)
|
||||
}
|
|
@ -4,7 +4,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/youtube/vitess/go/vt/schema"
|
||||
"github.com/youtube/vitess/go/vt/tabletserver/querytypes"
|
||||
)
|
||||
|
||||
func Example() {
|
||||
|
@ -45,9 +44,8 @@ func Example() {
|
|||
splitter := NewSplitter(splitParams, algorithm)
|
||||
|
||||
// 4. Call splitter.Split() to Split the query.
|
||||
// The result is a slice of querytypes.QuerySplit objects.
|
||||
var queryParts []querytypes.QuerySplit
|
||||
queryParts, err = splitter.Split()
|
||||
// The result is a slice of querytypes.QuerySplit objects (and an error object).
|
||||
queryParts, err := splitter.Split()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("splitter.Split() failed with: %v", err))
|
||||
}
|
||||
|
|
|
@ -116,6 +116,10 @@ func NewSplitParamsGivenSplitCount(
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func (splitParams *SplitParams) GetSplitTableName() string {
|
||||
return splitParams.splitTableSchema.Name
|
||||
}
|
||||
|
||||
// newSplitParams validates and initializes all the fields except splitCount and
|
||||
// numRowsPerQueryPart. It contains the common code for the constructors above.
|
||||
func newSplitParams(sql string, bindVariables map[string]interface{}, splitColumns []string,
|
||||
|
|
|
@ -116,6 +116,17 @@ type TabletConn interface {
|
|||
// appending primary key range clauses to the original query
|
||||
SplitQuery(ctx context.Context, query querytypes.BoundQuery, splitColumn string, splitCount int64) ([]querytypes.QuerySplit, error)
|
||||
|
||||
// SplitQuery splits a query into equally sized smaller queries by
|
||||
// appending primary key range clauses to the original query
|
||||
// TODO(erez): Remove SplitQuery and rename this to SplitQueryV2 once migration is done.
|
||||
SplitQueryV2(
|
||||
ctx context.Context,
|
||||
query querytypes.BoundQuery,
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) (queries []querytypes.QuerySplit, err error)
|
||||
|
||||
// StreamHealth starts a streaming RPC for VTTablet health status updates.
|
||||
StreamHealth(ctx context.Context) (StreamHealthReader, error)
|
||||
}
|
||||
|
|
|
@ -637,6 +637,52 @@ func (f *FakeQueryService) SplitQuery(ctx context.Context, target *querypb.Targe
|
|||
return splitQueryQuerySplitList, nil
|
||||
}
|
||||
|
||||
// SplitQueryV2 is part of the queryservice.QueryService interface
|
||||
// TODO(erez): Rename to SplitQuery after migration to SplitQuery V2 is done.
|
||||
func (f *FakeQueryService) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
target *querypb.Target,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm,
|
||||
sessionID int64) ([]querytypes.QuerySplit, error) {
|
||||
|
||||
if f.hasError {
|
||||
return nil, testTabletError
|
||||
}
|
||||
if f.panics {
|
||||
panic(fmt.Errorf("test-triggered panic"))
|
||||
}
|
||||
f.checkSessionTargetCallerID(ctx, "SplitQuery", target, sessionID)
|
||||
if !reflect.DeepEqual(querytypes.BoundQuery{
|
||||
Sql: sql,
|
||||
BindVariables: bindVariables,
|
||||
}, splitQueryV2BoundQuery) {
|
||||
f.t.Errorf("invalid SplitQuery.SplitQueryRequest.Query: got %v expected %v",
|
||||
querytypes.QueryAsString(sql, bindVariables), splitQueryBoundQuery)
|
||||
}
|
||||
if !reflect.DeepEqual(splitColumns, splitQueryV2SplitColumns) {
|
||||
f.t.Errorf("invalid SplitQuery.SplitColumn: got %v expected %v",
|
||||
splitColumns, splitQueryV2SplitColumns)
|
||||
}
|
||||
if !reflect.DeepEqual(splitCount, splitQueryV2SplitCount) {
|
||||
f.t.Errorf("invalid SplitQuery.SplitCount: got %v expected %v",
|
||||
splitCount, splitQueryV2SplitCount)
|
||||
}
|
||||
if !reflect.DeepEqual(numRowsPerQueryPart, splitQueryV2NumRowsPerQueryPart) {
|
||||
f.t.Errorf("invalid SplitQuery.numRowsPerQueryPart: got %v expected %v",
|
||||
numRowsPerQueryPart, splitQueryV2NumRowsPerQueryPart)
|
||||
}
|
||||
if algorithm != splitQueryV2Algorithm {
|
||||
f.t.Errorf("invalid SplitQuery.algorithm: got %v expected %v",
|
||||
algorithm, splitQueryV2Algorithm)
|
||||
}
|
||||
return splitQueryQueryV2SplitList, nil
|
||||
}
|
||||
|
||||
var splitQueryBoundQuery = querytypes.BoundQuery{
|
||||
Sql: "splitQuery",
|
||||
BindVariables: map[string]interface{}{
|
||||
|
@ -658,6 +704,32 @@ var splitQueryQuerySplitList = []querytypes.QuerySplit{
|
|||
},
|
||||
}
|
||||
|
||||
// TODO(erez): Rename to SplitQuery after migration to SplitQuery V2 is done.
|
||||
var splitQueryV2SplitColumns = []string{"nice_column_to_split"}
|
||||
|
||||
const splitQueryV2SplitCount = 372
|
||||
|
||||
var splitQueryV2BoundQuery = querytypes.BoundQuery{
|
||||
Sql: "splitQuery",
|
||||
BindVariables: map[string]interface{}{
|
||||
"bind1": int64(43),
|
||||
},
|
||||
}
|
||||
|
||||
const splitQueryV2NumRowsPerQueryPart = 123
|
||||
const splitQueryV2Algorithm = querypb.SplitQueryRequest_FULL_SCAN
|
||||
|
||||
var splitQueryQueryV2SplitList = []querytypes.QuerySplit{
|
||||
{
|
||||
Sql: "splitQuery",
|
||||
BindVariables: map[string]interface{}{
|
||||
"bind1": int64(43),
|
||||
"keyspace_id": int64(3333),
|
||||
},
|
||||
RowCount: 4456,
|
||||
},
|
||||
}
|
||||
|
||||
func testSplitQuery(t *testing.T, conn tabletconn.TabletConn) {
|
||||
ctx := context.Background()
|
||||
ctx = callerid.NewContext(ctx, testCallerID, testVTGateCallerID)
|
||||
|
@ -670,6 +742,26 @@ func testSplitQuery(t *testing.T, conn tabletconn.TabletConn) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(erez): Rename to SplitQuery after migration to SplitQuery V2 is done.
|
||||
func testSplitQueryV2(t *testing.T, conn tabletconn.TabletConn) {
|
||||
ctx := context.Background()
|
||||
ctx = callerid.NewContext(ctx, testCallerID, testVTGateCallerID)
|
||||
qsl, err := conn.SplitQueryV2(
|
||||
ctx,
|
||||
splitQueryV2BoundQuery,
|
||||
splitQueryV2SplitColumns,
|
||||
splitQueryV2SplitCount,
|
||||
splitQueryV2NumRowsPerQueryPart,
|
||||
splitQueryV2Algorithm,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("SplitQuery failed: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(qsl, splitQueryQueryV2SplitList) {
|
||||
t.Errorf("Unexpected result from SplitQuery: got %v wanted %v", qsl, splitQueryQuerySplitList)
|
||||
}
|
||||
}
|
||||
|
||||
func testSplitQueryError(t *testing.T, conn tabletconn.TabletConn) {
|
||||
ctx := context.Background()
|
||||
_, err := conn.SplitQuery(ctx, splitQueryBoundQuery, splitQuerySplitColumn, splitQuerySplitCount)
|
||||
|
|
|
@ -23,8 +23,12 @@ import (
|
|||
"github.com/youtube/vitess/go/vt/dbconfigs"
|
||||
"github.com/youtube/vitess/go/vt/dbconnpool"
|
||||
"github.com/youtube/vitess/go/vt/mysqlctl"
|
||||
"github.com/youtube/vitess/go/vt/schema"
|
||||
"github.com/youtube/vitess/go/vt/sqlparser"
|
||||
"github.com/youtube/vitess/go/vt/tabletserver/queryservice"
|
||||
"github.com/youtube/vitess/go/vt/tabletserver/querytypes"
|
||||
"github.com/youtube/vitess/go/vt/tabletserver/splitquery"
|
||||
"github.com/youtube/vitess/go/vt/utils"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
querypb "github.com/youtube/vitess/go/vt/proto/query"
|
||||
|
@ -662,6 +666,10 @@ func (tsv *TabletServer) handleExecErrorNoPanic(sql string, bindVariables map[st
|
|||
return myError
|
||||
case ErrFatal:
|
||||
logMethod = log.Errorf
|
||||
case ErrFail:
|
||||
// ErrFail is when we think the query itself is problematic. This doesn't
|
||||
// indicate a system or component wide degradation, so we log to INFO.
|
||||
logMethod = log.Infof
|
||||
}
|
||||
// We want to suppress/demote some MySQL error codes (regardless of the ErrorType)
|
||||
switch terr.SQLError {
|
||||
|
@ -797,6 +805,8 @@ func (tsv *TabletServer) ExecuteBatch(ctx context.Context, target *querypb.Targe
|
|||
|
||||
// SplitQuery splits a query + bind variables into smaller queries that return a
|
||||
// subset of rows from the original query.
|
||||
// TODO(erez): Remove this method and rename SplitQueryV2 to SplitQuery once we migrate to
|
||||
// SplitQuery V2.
|
||||
func (tsv *TabletServer) SplitQuery(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}, splitColumn string, splitCount int64, sessionID int64) (splits []querytypes.QuerySplit, err error) {
|
||||
logStats := newLogStats("SplitQuery", ctx)
|
||||
logStats.OriginalSQL = sql
|
||||
|
@ -844,6 +854,204 @@ func (tsv *TabletServer) SplitQuery(ctx context.Context, target *querypb.Target,
|
|||
return splits, nil
|
||||
}
|
||||
|
||||
// SplitQueryV2 splits a query + bind variables into smaller queries that return a
|
||||
// subset of rows from the original query. This is the new version that supports multiple
|
||||
// split columns and multiple split algortihms.
|
||||
// See the documentation of SplitQueryRequest in proto/vtgate.proto for more details.
|
||||
func (tsv *TabletServer) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
target *querypb.Target,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm,
|
||||
sessionID int64) (splits []querytypes.QuerySplit, err error) {
|
||||
|
||||
if err := validateSplitQueryParameters(
|
||||
sql,
|
||||
bindVariables,
|
||||
splitColumns,
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm,
|
||||
sessionID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(erez): ASSERT/Check that we are a rdonly tablet.
|
||||
logStats := newLogStats("SplitQuery", ctx)
|
||||
logStats.OriginalSQL = sql
|
||||
logStats.BindVariables = bindVariables
|
||||
defer handleError(&err, logStats, tsv.qe.queryServiceStats)
|
||||
if err = tsv.startRequest(target, sessionID, false, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx, cancel := withTimeout(ctx, tsv.QueryTimeout.Get())
|
||||
defer func() {
|
||||
cancel()
|
||||
tsv.endRequest(false)
|
||||
}()
|
||||
|
||||
schema := getSchemaForSplitQuery(tsv.qe.schemaInfo)
|
||||
var splitParams *splitquery.SplitParams
|
||||
switch {
|
||||
case numRowsPerQueryPart != 0 && splitCount == 0:
|
||||
splitParams, err = splitquery.NewSplitParamsGivenNumRowsPerQueryPart(
|
||||
sql, bindVariables, splitColumns, numRowsPerQueryPart, schema)
|
||||
case numRowsPerQueryPart == 0 && splitCount != 0:
|
||||
splitParams, err = splitquery.NewSplitParamsGivenSplitCount(
|
||||
sql, bindVariables, splitColumns, splitCount, schema)
|
||||
default:
|
||||
panic(fmt.Sprintf("Exactly one of {numRowsPerQueryPart, splitCount} must be"+
|
||||
" non zero. This should have already been caught by 'validateSplitQueryParameters' and "+
|
||||
" returned as an error. Got: numRowsPerQueryPart=%v, splitCount=%v. SQL: %v",
|
||||
numRowsPerQueryPart,
|
||||
splitCount,
|
||||
querytypes.QueryAsString(sql, bindVariables)))
|
||||
}
|
||||
// TODO(erez): Make the splitquery package return tabletserver errors.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func(start time.Time) {
|
||||
addUserTableQueryStats(
|
||||
tsv.qe.queryServiceStats,
|
||||
ctx,
|
||||
splitParams.GetSplitTableName(),
|
||||
"SplitQuery",
|
||||
int64(time.Now().Sub(start)))
|
||||
}(time.Now())
|
||||
|
||||
sqlExecuter := &splitQuerySQLExecuter{
|
||||
queryExecutor: &QueryExecutor{
|
||||
ctx: ctx,
|
||||
logStats: logStats,
|
||||
qe: tsv.qe,
|
||||
},
|
||||
}
|
||||
algorithmObject, err := createSplitQueryAlgorithmObject(algorithm, splitParams, sqlExecuter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO(erez): Make the splitquery package use Vitess error codes.
|
||||
return splitquery.NewSplitter(splitParams, algorithmObject).Split()
|
||||
}
|
||||
|
||||
// validateSplitQueryParameters perform some validations on the SplitQuery parameters
|
||||
// returns an error that can be returned to the user if a validation fails.
|
||||
func validateSplitQueryParameters(
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm,
|
||||
sessionID int64) error {
|
||||
if numRowsPerQueryPart < 0 {
|
||||
return NewTabletError(
|
||||
ErrFail,
|
||||
vtrpcpb.ErrorCode_BAD_INPUT,
|
||||
"splitQuery: numRowsPerQueryPart must be non-negative. Got: %v. SQL: %v",
|
||||
numRowsPerQueryPart,
|
||||
querytypes.QueryAsString(sql, bindVariables))
|
||||
}
|
||||
if splitCount < 0 {
|
||||
return NewTabletError(
|
||||
ErrFail,
|
||||
vtrpcpb.ErrorCode_BAD_INPUT,
|
||||
"splitQuery: splitCount must be non-negative. Got: %v. SQL: %v",
|
||||
splitCount,
|
||||
querytypes.QueryAsString(sql, bindVariables))
|
||||
}
|
||||
if (splitCount == 0 && numRowsPerQueryPart == 0) ||
|
||||
(splitCount != 0 && numRowsPerQueryPart != 0) {
|
||||
return NewTabletError(
|
||||
ErrFail,
|
||||
vtrpcpb.ErrorCode_BAD_INPUT,
|
||||
"splitQuery: exactly one of {numRowsPerQueryPart, splitCount} must be"+
|
||||
" non zero. Got: numRowsPerQueryPart=%v, splitCount=%v. SQL: %v",
|
||||
splitCount,
|
||||
querytypes.QueryAsString(sql, bindVariables))
|
||||
}
|
||||
if algorithm != querypb.SplitQueryRequest_EQUAL_SPLITS &&
|
||||
algorithm != querypb.SplitQueryRequest_FULL_SCAN {
|
||||
return NewTabletError(
|
||||
ErrFail,
|
||||
vtrpcpb.ErrorCode_BAD_INPUT,
|
||||
"splitquery: unsupported algorithm: %v. SQL: %v",
|
||||
algorithm,
|
||||
querytypes.QueryAsString(sql, bindVariables))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// splitQuerySQLExecuter implements splitquery.SQLExecuterInterface and allows the splitquery
|
||||
// package to send SQL statements to MySQL
|
||||
// TODO(erez): Find out what's the correct way of doing this.
|
||||
// We need to parse the query since we're dealing with bind-vars.
|
||||
type splitQuerySQLExecuter struct {
|
||||
queryExecutor *QueryExecutor
|
||||
}
|
||||
|
||||
// TODO(erez): Add an SQLExecute() to SQLExecuterInterface that gets a parsed query so that
|
||||
// we don't have to parse the query again here.
|
||||
func (se *splitQuerySQLExecuter) SQLExecute(
|
||||
sql string, bindVariables map[string]interface{}) (*sqltypes.Result, error) {
|
||||
|
||||
ast, err := sqlparser.Parse(sql)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("splitQuerySQLExecuter: parsing sql failed with: %v", err)
|
||||
}
|
||||
parsedQuery := sqlparser.GenerateParsedQuery(ast)
|
||||
|
||||
conn, err := se.queryExecutor.getConn(se.queryExecutor.qe.connPool)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Recycle()
|
||||
|
||||
// TODO(erez): Find out what 'buildStreamComment' is, and see if we need to use it or comment why
|
||||
// we don't.
|
||||
// We clone "bindVariables" since fullFetch() changes it.
|
||||
return se.queryExecutor.fullFetch(
|
||||
conn,
|
||||
parsedQuery,
|
||||
utils.CloneBindVariables(bindVariables),
|
||||
nil /* buildStreamComment */)
|
||||
}
|
||||
|
||||
// getSchemaForSplitQuery converts the given schemaInfo object into
|
||||
// the datastructure needed by the splitquery package: a map from a table name
|
||||
// to its corresponding schame.Table object.
|
||||
func getSchemaForSplitQuery(schemaInfo *SchemaInfo) map[string]*schema.Table {
|
||||
// Get a snapshot of the schema.
|
||||
var tableList []*schema.Table = schemaInfo.GetSchema()
|
||||
result := make(map[string]*schema.Table, len(tableList))
|
||||
for _, table := range tableList {
|
||||
// TODO(erez): Panic if table.Name is already in 'result'.
|
||||
result[table.Name] = table
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func createSplitQueryAlgorithmObject(
|
||||
algorithm querypb.SplitQueryRequest_Algorithm,
|
||||
splitParams *splitquery.SplitParams,
|
||||
sqlExecuter splitquery.SQLExecuter) (splitquery.SplitAlgorithmInterface, error) {
|
||||
|
||||
switch algorithm {
|
||||
case querypb.SplitQueryRequest_FULL_SCAN:
|
||||
return splitquery.NewFullScanAlgorithm(splitParams, sqlExecuter)
|
||||
case querypb.SplitQueryRequest_EQUAL_SPLITS:
|
||||
return splitquery.NewEqualSplitsAlgorithm(splitParams, sqlExecuter)
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown algorithm enum: %+v", algorithm))
|
||||
}
|
||||
}
|
||||
|
||||
// StreamHealthRegister is part of queryservice.QueryService interface
|
||||
func (tsv *TabletServer) StreamHealthRegister(c chan<- *querypb.StreamHealthResponse) (int, error) {
|
||||
tsv.streamHealthMutex.Lock()
|
||||
|
|
|
@ -1042,6 +1042,51 @@ func TestTabletServerSplitQuery(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(erez): Rename to TestTabletServerSplitQuery once migration to SplitQuery is done.
|
||||
func TestTabletServerSplitQueryV2(t *testing.T) {
|
||||
db := setUpTabletServerTest()
|
||||
db.AddQuery("SELECT MIN(pk), MAX(pk) FROM test_table", &sqltypes.Result{
|
||||
Fields: []*querypb.Field{
|
||||
{Name: "pk", Type: sqltypes.Int32},
|
||||
},
|
||||
RowsAffected: 1,
|
||||
Rows: [][]sqltypes.Value{
|
||||
{
|
||||
sqltypes.MakeTrusted(sqltypes.Int32, []byte("1")),
|
||||
sqltypes.MakeTrusted(sqltypes.Int32, []byte("100")),
|
||||
},
|
||||
},
|
||||
})
|
||||
testUtils := newTestUtils()
|
||||
config := testUtils.newQueryServiceConfig()
|
||||
tsv := NewTabletServer(config)
|
||||
dbconfigs := testUtils.newDBConfigs(db)
|
||||
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
|
||||
err := tsv.StartService(target, dbconfigs, []SchemaOverride{}, testUtils.newMysqld(&dbconfigs))
|
||||
if err != nil {
|
||||
t.Fatalf("StartService failed: %v", err)
|
||||
}
|
||||
defer tsv.StopService()
|
||||
ctx := context.Background()
|
||||
sql := "select * from test_table where count > :count"
|
||||
splits, err := tsv.SplitQueryV2(
|
||||
ctx,
|
||||
nil, /* target */
|
||||
sql,
|
||||
nil, /* bindVariables */
|
||||
[]string{}, /* splitColumns */
|
||||
10, /* splitCount */
|
||||
0, /* numRowsPerQueryPart */
|
||||
querypb.SplitQueryRequest_EQUAL_SPLITS,
|
||||
tsv.sessionID)
|
||||
if err != nil {
|
||||
t.Fatalf("TabletServer.SplitQuery should succeed: %v, but get error: %v", sql, err)
|
||||
}
|
||||
if len(splits) != 10 {
|
||||
t.Fatalf("got: %v, want: %v.\nsplits: %+v", len(splits), 10, splits)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTabletServerSplitQueryInvalidQuery(t *testing.T) {
|
||||
db := setUpTabletServerTest()
|
||||
db.AddQuery("SELECT MIN(pk), MAX(pk) FROM test_table", &sqltypes.Result{
|
||||
|
@ -1084,7 +1129,40 @@ func TestTabletServerSplitQueryInvalidQuery(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(erez): Rename to TestTabletServerSplitQueryInvalidQuery once migration to SplitQuery
|
||||
// is done.
|
||||
func TestTabletServerSplitQueryV2InvalidQuery(t *testing.T) {
|
||||
db := setUpTabletServerTest()
|
||||
testUtils := newTestUtils()
|
||||
config := testUtils.newQueryServiceConfig()
|
||||
tsv := NewTabletServer(config)
|
||||
dbconfigs := testUtils.newDBConfigs(db)
|
||||
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
|
||||
err := tsv.StartService(target, dbconfigs, []SchemaOverride{}, testUtils.newMysqld(&dbconfigs))
|
||||
if err != nil {
|
||||
t.Fatalf("StartService failed: %v", err)
|
||||
}
|
||||
defer tsv.StopService()
|
||||
ctx := context.Background()
|
||||
// SplitQuery should not support SQLs with a LIMIT clause:
|
||||
sql := "select * from test_table where count > :count limit 10"
|
||||
_, err = tsv.SplitQueryV2(
|
||||
ctx,
|
||||
nil, /* target */
|
||||
sql,
|
||||
nil, /* bindVariables */
|
||||
[]string{}, /* splitColumns */
|
||||
10, /* splitCount */
|
||||
0, /* numRowsPerQueryPart */
|
||||
querypb.SplitQueryRequest_EQUAL_SPLITS,
|
||||
tsv.sessionID)
|
||||
if err == nil {
|
||||
t.Fatalf("TabletServer.SplitQuery should fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTabletServerSplitQueryInvalidMinMax(t *testing.T) {
|
||||
// Tests that split query returns an error when the query is invalid.
|
||||
db := setUpTabletServerTest()
|
||||
testUtils := newTestUtils()
|
||||
pkMinMaxQuery := "SELECT MIN(pk), MAX(pk) FROM test_table"
|
||||
|
@ -1129,6 +1207,38 @@ func TestTabletServerSplitQueryInvalidMinMax(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(erez): Rename to TestTabletServerSplitQueryInvalidParams once migration to SplitQuery
|
||||
// is done.
|
||||
func TestTabletServerSplitQueryV2InvalidParams(t *testing.T) {
|
||||
// Tests that SplitQuery returns an error when both numRowsPerQueryPart and splitCount are given.
|
||||
db := setUpTabletServerTest()
|
||||
testUtils := newTestUtils()
|
||||
config := testUtils.newQueryServiceConfig()
|
||||
tsv := NewTabletServer(config)
|
||||
dbconfigs := testUtils.newDBConfigs(db)
|
||||
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
|
||||
err := tsv.StartService(target, dbconfigs, []SchemaOverride{}, testUtils.newMysqld(&dbconfigs))
|
||||
if err != nil {
|
||||
t.Fatalf("StartService failed: %v", err)
|
||||
}
|
||||
defer tsv.StopService()
|
||||
ctx := context.Background()
|
||||
sql := "select * from test_table where count > :count"
|
||||
_, err = tsv.SplitQueryV2(
|
||||
ctx,
|
||||
nil, /* target */
|
||||
sql,
|
||||
nil, /* bindVariables */
|
||||
[]string{}, /* splitColumns */
|
||||
10, /* splitCount */
|
||||
11, /* numRowsPerQueryPart */
|
||||
querypb.SplitQueryRequest_EQUAL_SPLITS,
|
||||
tsv.sessionID)
|
||||
if err == nil {
|
||||
t.Fatalf("TabletServer.SplitQuery should fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleExecUnknownError(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logStats := newLogStats("TestHandleExecError", ctx)
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package utils
|
||||
|
||||
// utils.go contains general utility functions used in the splitquery package.
|
||||
|
||||
// cloneBindVariables returns a shallow-copy of the given bindVariables map.
|
||||
func CloneBindVariables(bindVariables map[string]interface{}) map[string]interface{} {
|
||||
result := make(map[string]interface{}, len(bindVariables))
|
||||
for key, value := range bindVariables {
|
||||
result[key] = value
|
||||
}
|
||||
return result
|
||||
}
|
|
@ -22,6 +22,7 @@ type fakeVTGateService struct {
|
|||
type queryExecute struct {
|
||||
SQL string
|
||||
BindVariables map[string]interface{}
|
||||
Keyspace string
|
||||
TabletType topodatapb.TabletType
|
||||
Session *vtgatepb.Session
|
||||
NotInTransaction bool
|
||||
|
@ -36,7 +37,7 @@ type queryExecuteSpecificShard struct {
|
|||
}
|
||||
|
||||
// Execute is part of the VTGateService interface
|
||||
func (f *fakeVTGateService) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
func (f *fakeVTGateService) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
execCase, ok := execMap[sql]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no match for: %s", sql)
|
||||
|
@ -44,6 +45,7 @@ func (f *fakeVTGateService) Execute(ctx context.Context, sql string, bindVariabl
|
|||
query := &queryExecute{
|
||||
SQL: sql,
|
||||
BindVariables: bindVariables,
|
||||
Keyspace: keyspace,
|
||||
TabletType: tabletType,
|
||||
Session: session,
|
||||
NotInTransaction: notInTransaction,
|
||||
|
@ -109,7 +111,7 @@ func (f *fakeVTGateService) ExecuteBatchKeyspaceIds(ctx context.Context, queries
|
|||
}
|
||||
|
||||
// StreamExecute is part of the VTGateService interface
|
||||
func (f *fakeVTGateService) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
func (f *fakeVTGateService) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
execCase, ok := execMap[sql]
|
||||
if !ok {
|
||||
return fmt.Errorf("no match for: %s", sql)
|
||||
|
@ -117,6 +119,7 @@ func (f *fakeVTGateService) StreamExecute(ctx context.Context, sql string, bindV
|
|||
query := &queryExecute{
|
||||
SQL: sql,
|
||||
BindVariables: bindVariables,
|
||||
Keyspace: keyspace,
|
||||
TabletType: tabletType,
|
||||
}
|
||||
if !reflect.DeepEqual(query, execCase.execQuery) {
|
||||
|
@ -214,6 +217,21 @@ func (f *fakeVTGateService) SplitQuery(ctx context.Context, keyspace string, sql
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// SplitQueryV2 is part of the VTGateService interface
|
||||
// TODO(erez): Rename to SplitQuery after migration to SplitQuery V2 is done.
|
||||
func (f *fakeVTGateService) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
keyspace string,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm,
|
||||
) ([]*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetSrvKeyspace is part of the VTGateService interface
|
||||
func (f *fakeVTGateService) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) {
|
||||
return &topodatapb.SrvKeyspace{}, nil
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/youtube/vitess/go/vt/vterrors"
|
||||
"github.com/youtube/vitess/go/vt/vtgate/txbuffer"
|
||||
|
||||
querypb "github.com/youtube/vitess/go/vt/proto/query"
|
||||
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
|
||||
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
|
||||
)
|
||||
|
@ -142,7 +143,9 @@ func (dg *discoveryGateway) Begin(ctx context.Context, keyspace string, shard st
|
|||
err = dg.withRetry(ctx, keyspace, shard, tabletType, func(conn tabletconn.TabletConn) error {
|
||||
var innerErr error
|
||||
// Potentially buffer this transaction.
|
||||
txbuffer.FakeBuffer(keyspace, shard, attemptNumber)
|
||||
if bufferErr := txbuffer.FakeBuffer(keyspace, shard, attemptNumber); bufferErr != nil {
|
||||
return bufferErr
|
||||
}
|
||||
transactionID, innerErr = conn.Begin(ctx)
|
||||
attemptNumber++
|
||||
return innerErr
|
||||
|
@ -177,6 +180,31 @@ func (dg *discoveryGateway) SplitQuery(ctx context.Context, keyspace, shard stri
|
|||
return
|
||||
}
|
||||
|
||||
// SplitQuery splits a query into sub-queries for the specified keyspace, shard, and tablet type.
|
||||
// TODO(erez): Rename to SplitQuery after migration to SplitQuery V2.
|
||||
func (dg *discoveryGateway) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
keyspace,
|
||||
shard string,
|
||||
tabletType topodatapb.TabletType,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) (queries []querytypes.QuerySplit, err error) {
|
||||
|
||||
err = dg.withRetry(ctx, keyspace, shard, tabletType, func(conn tabletconn.TabletConn) error {
|
||||
var innerErr error
|
||||
queries, innerErr = conn.SplitQueryV2(ctx, querytypes.BoundQuery{
|
||||
Sql: sql,
|
||||
BindVariables: bindVariables,
|
||||
}, splitColumns, splitCount, numRowsPerQueryPart, algorithm)
|
||||
return innerErr
|
||||
}, 0, false)
|
||||
return
|
||||
}
|
||||
|
||||
// Close shuts down underlying connections.
|
||||
func (dg *discoveryGateway) Close(ctx context.Context) error {
|
||||
for _, ctw := range dg.tabletsWatchers {
|
||||
|
|
|
@ -62,16 +62,36 @@ type querySplitQuery struct {
|
|||
SplitCount int64
|
||||
}
|
||||
|
||||
// TODO(erez): Rename after migration to SplitQuery V2 is done.
|
||||
type querySplitQueryV2 struct {
|
||||
Keyspace string
|
||||
SQL string
|
||||
BindVariables map[string]interface{}
|
||||
SplitColumns []string
|
||||
SplitCount int64
|
||||
NumRowsPerQueryPart int64
|
||||
Algorithm querypb.SplitQueryRequest_Algorithm
|
||||
}
|
||||
|
||||
type splitQueryResponse struct {
|
||||
splitQuery *querySplitQuery
|
||||
reply []*vtgatepb.SplitQueryResponse_Part
|
||||
err error
|
||||
}
|
||||
|
||||
// TODO(erez): Rename after migration to SplitQuery V2 is done.
|
||||
type splitQueryV2Response struct {
|
||||
splitQuery *querySplitQueryV2
|
||||
reply []*vtgatepb.SplitQueryResponse_Part
|
||||
err error
|
||||
}
|
||||
|
||||
// FakeVTGateConn provides a fake implementation of vtgateconn.Impl
|
||||
type FakeVTGateConn struct {
|
||||
execMap map[string]*queryResponse
|
||||
splitQueryMap map[string]*splitQueryResponse
|
||||
// TODO(erez): Rename after migration to SplitQuery V2 is done.
|
||||
splitQueryV2Map map[string]*splitQueryV2Response
|
||||
}
|
||||
|
||||
// RegisterFakeVTGateConnDialer registers the proper dialer for this fake,
|
||||
|
@ -80,8 +100,9 @@ type FakeVTGateConn struct {
|
|||
func RegisterFakeVTGateConnDialer() (*FakeVTGateConn, string) {
|
||||
protocol := "fake"
|
||||
impl := &FakeVTGateConn{
|
||||
execMap: make(map[string]*queryResponse),
|
||||
splitQueryMap: make(map[string]*splitQueryResponse),
|
||||
execMap: make(map[string]*queryResponse),
|
||||
splitQueryMap: make(map[string]*splitQueryResponse),
|
||||
splitQueryV2Map: make(map[string]*splitQueryV2Response),
|
||||
}
|
||||
vtgateconn.RegisterDialer(protocol, func(ctx context.Context, address string, timeout time.Duration) (vtgateconn.Impl, error) {
|
||||
return impl, nil
|
||||
|
@ -141,6 +162,7 @@ func (conn *FakeVTGateConn) AddSplitQuery(
|
|||
splitColumn string,
|
||||
splitCount int64,
|
||||
expectedResult []*vtgatepb.SplitQueryResponse_Part) {
|
||||
|
||||
reply := make([]*vtgatepb.SplitQueryResponse_Part, splitCount)
|
||||
copy(reply, expectedResult)
|
||||
key := getSplitQueryKey(keyspace, sql, splitColumn, splitCount)
|
||||
|
@ -157,6 +179,36 @@ func (conn *FakeVTGateConn) AddSplitQuery(
|
|||
}
|
||||
}
|
||||
|
||||
// AddSplitQueryV2 adds a split query and expected result.
|
||||
// TODO(erez): Rename after migration to SplitQuery V2 is done.
|
||||
func (conn *FakeVTGateConn) AddSplitQueryV2(
|
||||
keyspace string,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm,
|
||||
expectedResult []*vtgatepb.SplitQueryResponse_Part) {
|
||||
|
||||
reply := make([]*vtgatepb.SplitQueryResponse_Part, len(expectedResult))
|
||||
copy(reply, expectedResult)
|
||||
key := getSplitQueryV2Key(keyspace, sql, splitColumns, splitCount, numRowsPerQueryPart, algorithm)
|
||||
conn.splitQueryV2Map[key] = &splitQueryV2Response{
|
||||
splitQuery: &querySplitQueryV2{
|
||||
Keyspace: keyspace,
|
||||
SQL: sql,
|
||||
BindVariables: bindVariables,
|
||||
SplitColumns: splitColumns,
|
||||
SplitCount: splitCount,
|
||||
NumRowsPerQueryPart: numRowsPerQueryPart,
|
||||
Algorithm: algorithm,
|
||||
},
|
||||
reply: reply,
|
||||
err: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute please see vtgateconn.Impl.Execute
|
||||
func (conn *FakeVTGateConn) Execute(ctx context.Context, sql string, bindVars map[string]interface{}, tabletType topodatapb.TabletType, session interface{}) (*sqltypes.Result, interface{}, error) {
|
||||
var s *vtgatepb.Session
|
||||
|
@ -352,7 +404,36 @@ func (conn *FakeVTGateConn) SplitQuery(ctx context.Context, keyspace string, que
|
|||
return reply, nil
|
||||
}
|
||||
|
||||
// GetSrvKeyspace please see vtgateconn.Impl.SplitQuery
|
||||
// SplitQueryV2 please see vtgateconn.Impl.SplitQueryV2
|
||||
// TODO(erez): Rename after migration to SplitQuery V2 is done.
|
||||
func (conn *FakeVTGateConn) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
keyspace string,
|
||||
query string,
|
||||
bindVars map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
|
||||
response, ok := conn.splitQueryV2Map[getSplitQueryV2Key(
|
||||
keyspace, query, splitColumns, splitCount, numRowsPerQueryPart, algorithm)]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(
|
||||
"no match for keyspace: %s,"+
|
||||
" query: %v,"+
|
||||
" splitColumns: %v,"+
|
||||
" splitCount: %v"+
|
||||
" numRowsPerQueryPart: %v"+
|
||||
" algorithm: %v",
|
||||
keyspace, query, splitColumns, splitCount, numRowsPerQueryPart, algorithm)
|
||||
}
|
||||
reply := make([]*vtgatepb.SplitQueryResponse_Part, len(response.reply))
|
||||
copy(reply, response.reply)
|
||||
return reply, nil
|
||||
}
|
||||
|
||||
// GetSrvKeyspace please see vtgateconn.Impl.GetSrvKeyspace
|
||||
func (conn *FakeVTGateConn) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) {
|
||||
return nil, fmt.Errorf("NYI")
|
||||
}
|
||||
|
@ -370,6 +451,24 @@ func getSplitQueryKey(keyspace string, query string, splitColumn string, splitCo
|
|||
return fmt.Sprintf("%s:%v:%v:%d", keyspace, query, splitColumn, splitCount)
|
||||
}
|
||||
|
||||
// TODO(erez): Rename after migration to SplitQuery V2 is done.
|
||||
func getSplitQueryV2Key(
|
||||
keyspace string,
|
||||
query string,
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) string {
|
||||
return fmt.Sprintf(
|
||||
"%v:%v:%v:%v:%v:%v",
|
||||
keyspace,
|
||||
query,
|
||||
splitColumns,
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm)
|
||||
}
|
||||
|
||||
func newSession(
|
||||
inTransaction bool,
|
||||
keyspace string,
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/youtube/vitess/go/vt/tabletserver/querytypes"
|
||||
"github.com/youtube/vitess/go/vt/topo"
|
||||
|
||||
querypb "github.com/youtube/vitess/go/vt/proto/query"
|
||||
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
|
||||
)
|
||||
|
||||
|
@ -55,6 +56,20 @@ type Gateway interface {
|
|||
// SplitQuery splits a query into sub-queries for the specified keyspace, shard, and tablet type.
|
||||
SplitQuery(ctx context.Context, keyspace, shard string, tabletType topodatapb.TabletType, sql string, bindVariables map[string]interface{}, splitColumn string, splitCount int64) ([]querytypes.QuerySplit, error)
|
||||
|
||||
// SplitQuery splits a query into sub-queries for the specified keyspace, shard, and tablet type.
|
||||
// TODO(erez): Rename to SplitQuery after migration to SplitQuery V2.
|
||||
SplitQueryV2(
|
||||
ctx context.Context,
|
||||
keyspace,
|
||||
shard string,
|
||||
tabletType topodatapb.TabletType,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) ([]querytypes.QuerySplit, error)
|
||||
|
||||
// Close shuts down underlying connections.
|
||||
Close(ctx context.Context) error
|
||||
|
||||
|
@ -128,22 +143,60 @@ type GatewayEndPointCacheStatus struct {
|
|||
AvgLatency float64 // in milliseconds
|
||||
}
|
||||
|
||||
const (
|
||||
aggrChanSize = 10000
|
||||
)
|
||||
|
||||
var (
|
||||
// aggrChan buffers queryInfo objects to be processed.
|
||||
aggrChan chan *queryInfo
|
||||
// muAggr protects below vars.
|
||||
muAggr sync.Mutex
|
||||
// aggregators holds all Aggregators created.
|
||||
aggregators []*GatewayEndPointStatusAggregator
|
||||
// gatewayStatsChanFull tracks the number of times
|
||||
// aggrChan becomes full.
|
||||
gatewayStatsChanFull *stats.Int
|
||||
)
|
||||
|
||||
func init() {
|
||||
// init global goroutines to aggregate stats.
|
||||
aggrChan = make(chan *queryInfo, aggrChanSize)
|
||||
gatewayStatsChanFull = stats.NewInt("GatewayStatsChanFullCount")
|
||||
go resetAggregators()
|
||||
go processQueryInfo()
|
||||
}
|
||||
|
||||
// registerAggregator registers an aggregator to the global list.
|
||||
func registerAggregator(a *GatewayEndPointStatusAggregator) {
|
||||
muAggr.Lock()
|
||||
defer muAggr.Unlock()
|
||||
aggregators = append(aggregators, a)
|
||||
}
|
||||
|
||||
// resetAggregators resets the next stats slot for all aggregators every second.
|
||||
func resetAggregators() {
|
||||
ticker := time.NewTicker(time.Second)
|
||||
for range ticker.C {
|
||||
muAggr.Lock()
|
||||
for _, a := range aggregators {
|
||||
a.resetNextSlot()
|
||||
}
|
||||
muAggr.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// processQueryInfo processes the next queryInfo object.
|
||||
func processQueryInfo() {
|
||||
for qi := range aggrChan {
|
||||
qi.aggr.processQueryInfo(qi)
|
||||
}
|
||||
}
|
||||
|
||||
// NewGatewayEndPointStatusAggregator creates a GatewayEndPointStatusAggregator.
|
||||
func NewGatewayEndPointStatusAggregator() *GatewayEndPointStatusAggregator {
|
||||
gepsa := &GatewayEndPointStatusAggregator{
|
||||
qiChan: make(chan *queryInfo, 10000),
|
||||
}
|
||||
go func() {
|
||||
ticker := time.NewTicker(time.Second)
|
||||
for range ticker.C {
|
||||
gepsa.resetNextSlot()
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
for qi := range gepsa.qiChan {
|
||||
gepsa.processQueryInfo(qi)
|
||||
}
|
||||
}()
|
||||
gepsa := &GatewayEndPointStatusAggregator{}
|
||||
registerAggregator(gepsa)
|
||||
return gepsa
|
||||
}
|
||||
|
||||
|
@ -154,7 +207,6 @@ type GatewayEndPointStatusAggregator struct {
|
|||
TabletType topodatapb.TabletType
|
||||
Name string // the alternative name of an endpoint
|
||||
Addr string // the host:port of an endpoint
|
||||
qiChan chan *queryInfo
|
||||
|
||||
// mu protects below fields.
|
||||
mu sync.RWMutex
|
||||
|
@ -167,6 +219,7 @@ type GatewayEndPointStatusAggregator struct {
|
|||
}
|
||||
|
||||
type queryInfo struct {
|
||||
aggr *GatewayEndPointStatusAggregator
|
||||
addr string
|
||||
tabletType topodatapb.TabletType
|
||||
elapsed time.Duration
|
||||
|
@ -176,20 +229,24 @@ type queryInfo struct {
|
|||
// UpdateQueryInfo updates the aggregator with the given information about a query.
|
||||
func (gepsa *GatewayEndPointStatusAggregator) UpdateQueryInfo(addr string, tabletType topodatapb.TabletType, elapsed time.Duration, hasError bool) {
|
||||
qi := &queryInfo{
|
||||
aggr: gepsa,
|
||||
addr: addr,
|
||||
tabletType: tabletType,
|
||||
elapsed: elapsed,
|
||||
hasError: hasError,
|
||||
}
|
||||
select {
|
||||
case gepsa.qiChan <- qi:
|
||||
case aggrChan <- qi:
|
||||
default:
|
||||
gatewayStatsChanFull.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (gepsa *GatewayEndPointStatusAggregator) processQueryInfo(qi *queryInfo) {
|
||||
gepsa.mu.Lock()
|
||||
defer gepsa.mu.Unlock()
|
||||
if gepsa.TabletType != qi.tabletType {
|
||||
gepsa.TabletType = qi.tabletType
|
||||
// reset counters
|
||||
gepsa.QueryCount = 0
|
||||
gepsa.QueryError = 0
|
||||
|
@ -203,14 +260,12 @@ func (gepsa *GatewayEndPointStatusAggregator) processQueryInfo(qi *queryInfo) {
|
|||
if qi.addr != "" {
|
||||
gepsa.Addr = qi.addr
|
||||
}
|
||||
gepsa.TabletType = qi.tabletType
|
||||
gepsa.QueryCount++
|
||||
gepsa.queryCountInMinute[gepsa.tick]++
|
||||
gepsa.latencyInMinute[gepsa.tick] += qi.elapsed
|
||||
if qi.hasError {
|
||||
gepsa.QueryError++
|
||||
}
|
||||
gepsa.mu.Unlock()
|
||||
}
|
||||
|
||||
// GetCacheStatus returns a GatewayEndPointCacheStatus representing the current gateway status.
|
||||
|
|
|
@ -19,20 +19,37 @@ func TestGatwayEndPointStatusAggregator(t *testing.T) {
|
|||
TabletType: topodatapb.TabletType_REPLICA,
|
||||
Name: "n",
|
||||
Addr: "a",
|
||||
qiChan: make(chan *queryInfo, 10000),
|
||||
}
|
||||
t.Logf("aggr = GatwayEndPointStatusAggregator{k, s, replica, n, a}")
|
||||
aggr.UpdateQueryInfo("", topodatapb.TabletType_REPLICA, 10*time.Millisecond, false)
|
||||
aggr.processQueryInfo(<-aggr.qiChan)
|
||||
t.Logf("aggr.UpdateQueryInfo(, replica, 10ms, false)")
|
||||
qi := &queryInfo{
|
||||
aggr: aggr,
|
||||
addr: "",
|
||||
tabletType: topodatapb.TabletType_REPLICA,
|
||||
elapsed: 10 * time.Millisecond,
|
||||
hasError: false,
|
||||
}
|
||||
aggr.processQueryInfo(qi)
|
||||
t.Logf("aggr.processQueryInfo(, replica, 10ms, false)")
|
||||
aggr.resetNextSlot()
|
||||
t.Logf("aggr.resetNextSlot()")
|
||||
aggr.UpdateQueryInfo("", topodatapb.TabletType_REPLICA, 8*time.Millisecond, false)
|
||||
aggr.processQueryInfo(<-aggr.qiChan)
|
||||
t.Logf("aggr.UpdateQueryInfo(, replica, 8ms, false)")
|
||||
aggr.UpdateQueryInfo("", topodatapb.TabletType_REPLICA, 3*time.Millisecond, true)
|
||||
aggr.processQueryInfo(<-aggr.qiChan)
|
||||
t.Logf("aggr.UpdateQueryInfo(, replica, 3ms, true)")
|
||||
qi = &queryInfo{
|
||||
aggr: aggr,
|
||||
addr: "",
|
||||
tabletType: topodatapb.TabletType_REPLICA,
|
||||
elapsed: 8 * time.Millisecond,
|
||||
hasError: false,
|
||||
}
|
||||
aggr.processQueryInfo(qi)
|
||||
t.Logf("aggr.processQueryInfo(, replica, 8ms, false)")
|
||||
qi = &queryInfo{
|
||||
aggr: aggr,
|
||||
addr: "",
|
||||
tabletType: topodatapb.TabletType_REPLICA,
|
||||
elapsed: 3 * time.Millisecond,
|
||||
hasError: true,
|
||||
}
|
||||
aggr.processQueryInfo(qi)
|
||||
t.Logf("aggr.processQueryInfo(, replica, 3ms, true)")
|
||||
want := &GatewayEndPointCacheStatus{
|
||||
Keyspace: "k",
|
||||
Shard: "s",
|
||||
|
@ -53,12 +70,24 @@ func TestGatwayEndPointStatusAggregator(t *testing.T) {
|
|||
aggr.resetNextSlot()
|
||||
}
|
||||
t.Logf("59 aggr.resetNextSlot()")
|
||||
aggr.UpdateQueryInfo("b", topodatapb.TabletType_MASTER, 9*time.Millisecond, false)
|
||||
aggr.processQueryInfo(<-aggr.qiChan)
|
||||
t.Logf("aggr.UpdateQueryInfo(b, master, 9ms, false)")
|
||||
aggr.UpdateQueryInfo("", topodatapb.TabletType_MASTER, 6*time.Millisecond, true)
|
||||
aggr.processQueryInfo(<-aggr.qiChan)
|
||||
t.Logf("aggr.UpdateQueryInfo(, master, 6ms, true)")
|
||||
qi = &queryInfo{
|
||||
aggr: aggr,
|
||||
addr: "b",
|
||||
tabletType: topodatapb.TabletType_MASTER,
|
||||
elapsed: 9 * time.Millisecond,
|
||||
hasError: false,
|
||||
}
|
||||
aggr.processQueryInfo(qi)
|
||||
t.Logf("aggr.processQueryInfo(b, master, 9ms, false)")
|
||||
qi = &queryInfo{
|
||||
aggr: aggr,
|
||||
addr: "",
|
||||
tabletType: topodatapb.TabletType_MASTER,
|
||||
elapsed: 6 * time.Millisecond,
|
||||
hasError: true,
|
||||
}
|
||||
aggr.processQueryInfo(qi)
|
||||
t.Logf("aggr.processQueryInfo(, master, 6ms, true)")
|
||||
want = &GatewayEndPointCacheStatus{
|
||||
Keyspace: "k",
|
||||
Shard: "s",
|
||||
|
|
|
@ -396,11 +396,45 @@ func (conn *vtgateConn) SplitQuery(ctx context.Context, keyspace string, query s
|
|||
}
|
||||
|
||||
request := &vtgatepb.SplitQueryRequest{
|
||||
CallerId: callerid.EffectiveCallerIDFromContext(ctx),
|
||||
Keyspace: keyspace,
|
||||
Query: q,
|
||||
SplitColumn: splitColumn,
|
||||
SplitCount: splitCount,
|
||||
CallerId: callerid.EffectiveCallerIDFromContext(ctx),
|
||||
Keyspace: keyspace,
|
||||
Query: q,
|
||||
SplitColumn: []string{splitColumn},
|
||||
SplitCount: splitCount,
|
||||
NumRowsPerQueryPart: 0,
|
||||
Algorithm: querypb.SplitQueryRequest_EQUAL_SPLITS,
|
||||
UseSplitQueryV2: false,
|
||||
}
|
||||
response, err := conn.c.SplitQuery(ctx, request)
|
||||
if err != nil {
|
||||
return nil, vterrors.FromGRPCError(err)
|
||||
}
|
||||
return response.Splits, nil
|
||||
}
|
||||
|
||||
func (conn *vtgateConn) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
keyspace string,
|
||||
query string,
|
||||
bindVars map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
q, err := querytypes.BoundQueryToProto3(query, bindVars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
request := &vtgatepb.SplitQueryRequest{
|
||||
CallerId: callerid.EffectiveCallerIDFromContext(ctx),
|
||||
Keyspace: keyspace,
|
||||
Query: q,
|
||||
SplitColumn: splitColumns,
|
||||
SplitCount: splitCount,
|
||||
NumRowsPerQueryPart: numRowsPerQueryPart,
|
||||
Algorithm: algorithm,
|
||||
UseSplitQueryV2: true,
|
||||
}
|
||||
response, err := conn.c.SplitQuery(ctx, request)
|
||||
if err != nil {
|
||||
|
|
|
@ -37,7 +37,7 @@ func (vtg *VTGate) Execute(ctx context.Context, request *vtgatepb.ExecuteRequest
|
|||
if err != nil {
|
||||
return nil, vterrors.ToGRPCError(err)
|
||||
}
|
||||
result, err := vtg.server.Execute(ctx, string(request.Query.Sql), bv, request.TabletType, request.Session, request.NotInTransaction)
|
||||
result, err := vtg.server.Execute(ctx, string(request.Query.Sql), bv, request.Keyspace, request.TabletType, request.Session, request.NotInTransaction)
|
||||
return &vtgatepb.ExecuteResponse{
|
||||
Result: sqltypes.ResultToProto3(result),
|
||||
Session: request.Session,
|
||||
|
@ -196,6 +196,7 @@ func (vtg *VTGate) StreamExecute(request *vtgatepb.StreamExecuteRequest, stream
|
|||
vtgErr := vtg.server.StreamExecute(ctx,
|
||||
string(request.Query.Sql),
|
||||
bv,
|
||||
request.Keyspace,
|
||||
request.TabletType,
|
||||
func(value *sqltypes.Result) error {
|
||||
return stream.Send(&vtgatepb.StreamExecuteResponse{
|
||||
|
@ -333,12 +334,17 @@ func (vtg *VTGate) SplitQuery(ctx context.Context, request *vtgatepb.SplitQueryR
|
|||
if err != nil {
|
||||
return nil, vterrors.ToGRPCError(err)
|
||||
}
|
||||
splits, vtgErr := vtg.server.SplitQuery(ctx,
|
||||
splits, vtgErr := vtgateservice.CallCorrectSplitQuery(
|
||||
vtg.server,
|
||||
request.UseSplitQueryV2,
|
||||
ctx,
|
||||
request.Keyspace,
|
||||
string(request.Query.Sql),
|
||||
bv,
|
||||
request.SplitColumn,
|
||||
request.SplitCount)
|
||||
request.SplitCount,
|
||||
request.NumRowsPerQueryPart,
|
||||
request.Algorithm)
|
||||
if vtgErr != nil {
|
||||
return nil, vterrors.ToGRPCError(vtgErr)
|
||||
}
|
||||
|
|
|
@ -68,9 +68,15 @@ type builder interface {
|
|||
SupplyCol(ref colref) int
|
||||
}
|
||||
|
||||
// VSchema defines the interface for this package to fetch
|
||||
// info about tables.
|
||||
type VSchema interface {
|
||||
Find(keyspace, tablename string) (table *vindexes.Table, err error)
|
||||
}
|
||||
|
||||
// Build builds a plan for a query based on the specified vschema.
|
||||
// It's the main entry point for this package.
|
||||
func Build(query string, vschema *vindexes.VSchema) (*engine.Plan, error) {
|
||||
func Build(query string, vschema VSchema) (*engine.Plan, error) {
|
||||
statement, err := sqlparser.Parse(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -14,15 +14,23 @@ import (
|
|||
"github.com/youtube/vitess/go/vt/vtgate/vindexes"
|
||||
)
|
||||
|
||||
// dmlFormatter strips out keyspace name from dmls.
|
||||
func dmlFormatter(buf *sqlparser.TrackedBuffer, node sqlparser.SQLNode) {
|
||||
switch node := node.(type) {
|
||||
case *sqlparser.TableName:
|
||||
node.Name.Format(buf)
|
||||
return
|
||||
}
|
||||
node.Format(buf)
|
||||
}
|
||||
|
||||
// buildUpdatePlan builds the instructions for an UPDATE statement.
|
||||
func buildUpdatePlan(upd *sqlparser.Update, vschema *vindexes.VSchema) (*engine.Route, error) {
|
||||
func buildUpdatePlan(upd *sqlparser.Update, vschema VSchema) (*engine.Route, error) {
|
||||
route := &engine.Route{
|
||||
Query: generateQuery(upd),
|
||||
}
|
||||
// We allow only one table in an update.
|
||||
tablename := sqlparser.GetTableName(upd.Table)
|
||||
var err error
|
||||
route.Table, err = vschema.FindTable(tablename)
|
||||
route.Table, err = vschema.Find(string(upd.Table.Qualifier), string(upd.Table.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -47,7 +55,7 @@ func buildUpdatePlan(upd *sqlparser.Update, vschema *vindexes.VSchema) (*engine.
|
|||
}
|
||||
|
||||
func generateQuery(statement sqlparser.Statement) string {
|
||||
buf := sqlparser.NewTrackedBuffer(nil)
|
||||
buf := sqlparser.NewTrackedBuffer(dmlFormatter)
|
||||
statement.Format(buf)
|
||||
return buf.String()
|
||||
}
|
||||
|
@ -68,14 +76,12 @@ func isIndexChanging(setClauses sqlparser.UpdateExprs, colVindexes []*vindexes.C
|
|||
}
|
||||
|
||||
// buildUpdatePlan builds the instructions for a DELETE statement.
|
||||
func buildDeletePlan(del *sqlparser.Delete, vschema *vindexes.VSchema) (*engine.Route, error) {
|
||||
func buildDeletePlan(del *sqlparser.Delete, vschema VSchema) (*engine.Route, error) {
|
||||
route := &engine.Route{
|
||||
Query: generateQuery(del),
|
||||
}
|
||||
// We allow only one table in a delete.
|
||||
tablename := sqlparser.GetTableName(del.Table)
|
||||
var err error
|
||||
route.Table, err = vschema.FindTable(tablename)
|
||||
route.Table, err = vschema.Find(string(del.Table.Qualifier), string(del.Table.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -13,12 +13,11 @@ import (
|
|||
"github.com/youtube/vitess/go/vt/vtgate/vindexes"
|
||||
)
|
||||
|
||||
// This file has functions to analyze the FROM clause
|
||||
// for select statements.
|
||||
// This file has functions to analyze the FROM clause.
|
||||
|
||||
// processTableExprs analyzes the FROM clause. It produces a builder
|
||||
// with all the routes identified.
|
||||
func processTableExprs(tableExprs sqlparser.TableExprs, vschema *vindexes.VSchema) (builder, error) {
|
||||
func processTableExprs(tableExprs sqlparser.TableExprs, vschema VSchema) (builder, error) {
|
||||
if len(tableExprs) != 1 {
|
||||
return nil, errors.New("unsupported: ',' join operator")
|
||||
}
|
||||
|
@ -26,7 +25,7 @@ func processTableExprs(tableExprs sqlparser.TableExprs, vschema *vindexes.VSchem
|
|||
}
|
||||
|
||||
// processTableExpr produces a builder subtree for the given TableExpr.
|
||||
func processTableExpr(tableExpr sqlparser.TableExpr, vschema *vindexes.VSchema) (builder, error) {
|
||||
func processTableExpr(tableExpr sqlparser.TableExpr, vschema VSchema) (builder, error) {
|
||||
switch tableExpr := tableExpr.(type) {
|
||||
case *sqlparser.AliasedTableExpr:
|
||||
return processAliasedTable(tableExpr, vschema)
|
||||
|
@ -55,7 +54,7 @@ func processTableExpr(tableExpr sqlparser.TableExpr, vschema *vindexes.VSchema)
|
|||
// vindex columns will be added to the tabsym.
|
||||
// A symtab symbol can only point to a route. This means that we canoot
|
||||
// support complex joins in subqueries yet.
|
||||
func processAliasedTable(tableExpr *sqlparser.AliasedTableExpr, vschema *vindexes.VSchema) (builder, error) {
|
||||
func processAliasedTable(tableExpr *sqlparser.AliasedTableExpr, vschema VSchema) (builder, error) {
|
||||
switch expr := tableExpr.Expr.(type) {
|
||||
case *sqlparser.TableName:
|
||||
eroute, table, err := getTablePlan(expr, vschema)
|
||||
|
@ -118,8 +117,8 @@ func processAliasedTable(tableExpr *sqlparser.AliasedTableExpr, vschema *vindexe
|
|||
// getTablePlan produces the initial engine.Route for the specified TableName.
|
||||
// It also returns the associated vschema info (*Table) so that
|
||||
// it can be used to create the symbol table entry.
|
||||
func getTablePlan(tableName *sqlparser.TableName, vschema *vindexes.VSchema) (*engine.Route, *vindexes.Table, error) {
|
||||
table, err := vschema.FindTable(string(tableName.Name))
|
||||
func getTablePlan(tableName *sqlparser.TableName, vschema VSchema) (*engine.Route, *vindexes.Table, error) {
|
||||
table, err := vschema.Find(string(tableName.Qualifier), string(tableName.Name))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -140,7 +139,7 @@ func getTablePlan(tableName *sqlparser.TableName, vschema *vindexes.VSchema) (*e
|
|||
// processJoin produces a builder subtree for the given Join.
|
||||
// If the left and right nodes can be part of the same route,
|
||||
// then it's a route. Otherwise, it's a join.
|
||||
func processJoin(ajoin *sqlparser.JoinTableExpr, vschema *vindexes.VSchema) (builder, error) {
|
||||
func processJoin(ajoin *sqlparser.JoinTableExpr, vschema VSchema) (builder, error) {
|
||||
switch ajoin.Join {
|
||||
case sqlparser.JoinStr, sqlparser.StraightJoinStr, sqlparser.LeftJoinStr:
|
||||
case sqlparser.RightJoinStr:
|
||||
|
|
|
@ -14,13 +14,12 @@ import (
|
|||
)
|
||||
|
||||
// buildInsertPlan builds the route for an INSERT statement.
|
||||
func buildInsertPlan(ins *sqlparser.Insert, vschema *vindexes.VSchema) (*engine.Route, error) {
|
||||
func buildInsertPlan(ins *sqlparser.Insert, vschema VSchema) (*engine.Route, error) {
|
||||
route := &engine.Route{
|
||||
Query: generateQuery(ins),
|
||||
}
|
||||
tablename := sqlparser.GetTableName(ins.Table)
|
||||
var err error
|
||||
route.Table, err = vschema.FindTable(tablename)
|
||||
route.Table, err = vschema.Find(string(ins.Table.Qualifier), string(ins.Table.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ type route struct {
|
|||
ERoute *engine.Route
|
||||
}
|
||||
|
||||
func newRoute(from sqlparser.TableExprs, eroute *engine.Route, table *vindexes.Table, vschema *vindexes.VSchema, alias, astName sqlparser.SQLName) *route {
|
||||
func newRoute(from sqlparser.TableExprs, eroute *engine.Route, table *vindexes.Table, vschema VSchema, alias, astName sqlparser.SQLName) *route {
|
||||
// We have some circular pointer references here:
|
||||
// The route points to the symtab idicating
|
||||
// the symtab that should be used to resolve symbols
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
)
|
||||
|
||||
// buildSelectPlan is the new function to build a Select plan.
|
||||
func buildSelectPlan(sel *sqlparser.Select, vschema *vindexes.VSchema) (primitive engine.Primitive, err error) {
|
||||
func buildSelectPlan(sel *sqlparser.Select, vschema VSchema) (primitive engine.Primitive, err error) {
|
||||
bindvars := getBindvars(sel)
|
||||
builder, err := processSelect(sel, vschema, nil)
|
||||
if err != nil {
|
||||
|
@ -43,7 +43,7 @@ func getBindvars(node sqlparser.SQLNode) map[string]struct{} {
|
|||
}
|
||||
|
||||
// processSelect builds a primitive tree for the given query or subquery.
|
||||
func processSelect(sel *sqlparser.Select, vschema *vindexes.VSchema, outer builder) (builder, error) {
|
||||
func processSelect(sel *sqlparser.Select, vschema VSchema, outer builder) (builder, error) {
|
||||
bldr, err := processTableExprs(sel.From, vschema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -50,12 +50,12 @@ type symtab struct {
|
|||
Colsyms []*colsym
|
||||
Externs []*sqlparser.ColName
|
||||
Outer *symtab
|
||||
VSchema *vindexes.VSchema
|
||||
VSchema VSchema
|
||||
}
|
||||
|
||||
// newSymtab creates a new symtab initialized
|
||||
// to contain the provided table alias.
|
||||
func newSymtab(vschema *vindexes.VSchema) *symtab {
|
||||
func newSymtab(vschema VSchema) *symtab {
|
||||
return &symtab{
|
||||
VSchema: vschema,
|
||||
}
|
||||
|
|
|
@ -45,14 +45,21 @@ func NewPlanner(vschema *vindexes.VSchema, cacheSize int) *Planner {
|
|||
|
||||
// GetPlan computes the plan for the given query. If one is in
|
||||
// the cache, it reuses it.
|
||||
func (plr *Planner) GetPlan(sql string) (*engine.Plan, error) {
|
||||
func (plr *Planner) GetPlan(sql, keyspace string) (*engine.Plan, error) {
|
||||
if plr.vschema == nil {
|
||||
return nil, errors.New("vschema not initialized")
|
||||
}
|
||||
if result, ok := plr.plans.Get(sql); ok {
|
||||
key := sql
|
||||
if keyspace != "" {
|
||||
key = keyspace + ":" + sql
|
||||
}
|
||||
if result, ok := plr.plans.Get(key); ok {
|
||||
return result.(*engine.Plan), nil
|
||||
}
|
||||
plan, err := planbuilder.Build(sql, plr.vschema)
|
||||
plan, err := planbuilder.Build(sql, &wrappedVSchema{
|
||||
vschema: plr.vschema,
|
||||
keyspace: keyspace,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -95,3 +102,15 @@ func (plr *Planner) ServeHTTP(response http.ResponseWriter, request *http.Reques
|
|||
response.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
type wrappedVSchema struct {
|
||||
vschema *vindexes.VSchema
|
||||
keyspace string
|
||||
}
|
||||
|
||||
func (vs *wrappedVSchema) Find(keyspace, tablename string) (table *vindexes.Table, err error) {
|
||||
if keyspace == "" {
|
||||
keyspace = vs.keyspace
|
||||
}
|
||||
return vs.vschema.Find(keyspace, tablename)
|
||||
}
|
||||
|
|
|
@ -17,17 +17,19 @@ type requestContext struct {
|
|||
ctx context.Context
|
||||
sql string
|
||||
bindVars map[string]interface{}
|
||||
keyspace string
|
||||
tabletType topodatapb.TabletType
|
||||
session *vtgatepb.Session
|
||||
notInTransaction bool
|
||||
router *Router
|
||||
}
|
||||
|
||||
func newRequestContext(ctx context.Context, sql string, bindVars map[string]interface{}, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, router *Router) *requestContext {
|
||||
func newRequestContext(ctx context.Context, sql string, bindVars map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool, router *Router) *requestContext {
|
||||
return &requestContext{
|
||||
ctx: ctx,
|
||||
sql: sql,
|
||||
bindVars: bindVars,
|
||||
keyspace: keyspace,
|
||||
tabletType: tabletType,
|
||||
session: session,
|
||||
notInTransaction: notInTransaction,
|
||||
|
@ -36,7 +38,9 @@ func newRequestContext(ctx context.Context, sql string, bindVars map[string]inte
|
|||
}
|
||||
|
||||
func (vc *requestContext) Execute(query string, bindvars map[string]interface{}) (*sqltypes.Result, error) {
|
||||
return vc.router.Execute(vc.ctx, query, bindvars, vc.tabletType, vc.session, false)
|
||||
// We have to use an empty keyspace here, becasue vindexes that call back can reference
|
||||
// any table.
|
||||
return vc.router.Execute(vc.ctx, query, bindvars, "", vc.tabletType, vc.session, false)
|
||||
}
|
||||
|
||||
func (vc *requestContext) ExecuteRoute(route *engine.Route, joinvars map[string]interface{}) (*sqltypes.Result, error) {
|
||||
|
|
|
@ -59,12 +59,12 @@ func NewRouter(serv topo.SrvTopoServer, cell string, vschema *vindexes.VSchema,
|
|||
}
|
||||
|
||||
// Execute routes a non-streaming query.
|
||||
func (rtr *Router) Execute(ctx context.Context, sql string, bindVars map[string]interface{}, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
func (rtr *Router) Execute(ctx context.Context, sql string, bindVars map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
if bindVars == nil {
|
||||
bindVars = make(map[string]interface{})
|
||||
}
|
||||
vcursor := newRequestContext(ctx, sql, bindVars, tabletType, session, notInTransaction, rtr)
|
||||
plan, err := rtr.planner.GetPlan(sql)
|
||||
vcursor := newRequestContext(ctx, sql, bindVars, keyspace, tabletType, session, notInTransaction, rtr)
|
||||
plan, err := rtr.planner.GetPlan(sql, keyspace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -72,12 +72,12 @@ func (rtr *Router) Execute(ctx context.Context, sql string, bindVars map[string]
|
|||
}
|
||||
|
||||
// StreamExecute executes a streaming query.
|
||||
func (rtr *Router) StreamExecute(ctx context.Context, sql string, bindVars map[string]interface{}, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
func (rtr *Router) StreamExecute(ctx context.Context, sql string, bindVars map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
if bindVars == nil {
|
||||
bindVars = make(map[string]interface{})
|
||||
}
|
||||
vcursor := newRequestContext(ctx, sql, bindVars, tabletType, nil, false, rtr)
|
||||
plan, err := rtr.planner.GetPlan(sql)
|
||||
vcursor := newRequestContext(ctx, sql, bindVars, keyspace, tabletType, nil, false, rtr)
|
||||
plan, err := rtr.planner.GetPlan(sql, keyspace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ var routerVSchema = createTestVSchema(`
|
|||
"Type": "numeric"
|
||||
}
|
||||
},
|
||||
"Classes": {
|
||||
"Tables": {
|
||||
"user": {
|
||||
"ColVindexes": [
|
||||
{
|
||||
|
@ -67,10 +67,10 @@ var routerVSchema = createTestVSchema(`
|
|||
"Name": "name_user_map"
|
||||
}
|
||||
],
|
||||
"Autoinc" : {
|
||||
"Col": "id",
|
||||
"Sequence": "user_seq"
|
||||
}
|
||||
"Autoinc" : {
|
||||
"Col": "id",
|
||||
"Sequence": "user_seq"
|
||||
}
|
||||
},
|
||||
"user_extra": {
|
||||
"ColVindexes": [
|
||||
|
@ -91,10 +91,10 @@ var routerVSchema = createTestVSchema(`
|
|||
"Name": "music_user_map"
|
||||
}
|
||||
],
|
||||
"Autoinc" : {
|
||||
"Col": "id",
|
||||
"Sequence": "user_seq"
|
||||
}
|
||||
"Autoinc" : {
|
||||
"Col": "id",
|
||||
"Sequence": "user_seq"
|
||||
}
|
||||
},
|
||||
"music_extra": {
|
||||
"ColVindexes": [
|
||||
|
@ -136,34 +136,22 @@ var routerVSchema = createTestVSchema(`
|
|||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"Tables": {
|
||||
"user": "user",
|
||||
"user_extra": "user_extra",
|
||||
"music": "music",
|
||||
"music_extra": "music_extra",
|
||||
"music_extra_reversed": "music_extra_reversed",
|
||||
"noauto_table": "noauto_table",
|
||||
"ksid_table": "ksid_table"
|
||||
}
|
||||
},
|
||||
"TestBadSharding": {
|
||||
"Sharded": false,
|
||||
"Tables": {
|
||||
"sharded_table": ""
|
||||
"sharded_table": {}
|
||||
}
|
||||
},
|
||||
"TestUnsharded": {
|
||||
"Sharded": false,
|
||||
"Classes": {
|
||||
"seq": {
|
||||
"Type": "Sequence"
|
||||
}
|
||||
},
|
||||
"Tables": {
|
||||
"user_seq": "seq",
|
||||
"music_user_map": "",
|
||||
"name_user_map": ""
|
||||
"user_seq": {
|
||||
"Type": "Sequence"
|
||||
},
|
||||
"music_user_map": {},
|
||||
"name_user_map": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,6 +203,7 @@ func routerExec(router *Router, sql string, bv map[string]interface{}) (*sqltype
|
|||
return router.Execute(context.Background(),
|
||||
sql,
|
||||
bv,
|
||||
"",
|
||||
topodatapb.TabletType_MASTER,
|
||||
nil,
|
||||
false)
|
||||
|
@ -222,7 +211,7 @@ func routerExec(router *Router, sql string, bv map[string]interface{}) (*sqltype
|
|||
|
||||
func routerStream(router *Router, sql string) (qr *sqltypes.Result, err error) {
|
||||
results := make(chan *sqltypes.Result, 10)
|
||||
err = router.StreamExecute(context.Background(), sql, nil, topodatapb.TabletType_MASTER, func(qr *sqltypes.Result) error {
|
||||
err = router.StreamExecute(context.Background(), sql, nil, "", topodatapb.TabletType_MASTER, func(qr *sqltypes.Result) error {
|
||||
results <- qr
|
||||
return nil
|
||||
})
|
||||
|
|
|
@ -562,6 +562,28 @@ func (sbc *sandboxConn) SplitQuery(ctx context.Context, query querytypes.BoundQu
|
|||
return splits, nil
|
||||
}
|
||||
|
||||
// Fake SplitQuery returns a single QuerySplit whose 'sql' field describes the received arguments.
|
||||
// TODO(erez): Rename to SplitQuery after the migration to SplitQuery V2 is done.
|
||||
func (sbc *sandboxConn) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
query querytypes.BoundQuery,
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) ([]querytypes.QuerySplit, error) {
|
||||
// The sandbox stores the shard name in the endPoint Host field.
|
||||
shard := sbc.endPoint.Host
|
||||
splits := []querytypes.QuerySplit{
|
||||
{
|
||||
Sql: fmt.Sprintf(
|
||||
"query:%v, splitColumns:%v, splitCount:%v,"+
|
||||
" numRowsPerQueryPart:%v, algorithm:%v, shard:%v",
|
||||
query, splitColumns, splitCount, numRowsPerQueryPart, algorithm, shard),
|
||||
},
|
||||
}
|
||||
return splits, nil
|
||||
}
|
||||
|
||||
// StreamHealth is not implemented.
|
||||
func (sbc *sandboxConn) StreamHealth(ctx context.Context) (tabletconn.StreamHealthReader, error) {
|
||||
return nil, fmt.Errorf("Not implemented in test")
|
||||
|
|
|
@ -587,6 +587,85 @@ func (stc *ScatterConn) SplitQueryCustomSharding(ctx context.Context, sql string
|
|||
return allSplits, nil
|
||||
}
|
||||
|
||||
// SplitQueryV2 scatters a SplitQueryV2 request to the shards whose names are given in 'shards'.
|
||||
// For every set of querytypes.QuerySplit's received from a shard, it applies the given
|
||||
// 'querySplitToPartFunc' function to convert each querytypes.QuerySplit into a
|
||||
// 'SplitQueryResponse_Part' message. Finally, it aggregates the obtained
|
||||
// SplitQueryResponse_Parts across all shards and returns the resulting slice.
|
||||
// TODO(erez): Remove 'scatterConn.SplitQuery' and rename this method to SplitQuery once
|
||||
// the migration to SplitQuery V2 is done.
|
||||
func (stc *ScatterConn) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
perShardSplitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm,
|
||||
shards []string,
|
||||
querySplitToQueryPartFunc func(
|
||||
querySplit *querytypes.QuerySplit, shard string) (*vtgatepb.SplitQueryResponse_Part, error),
|
||||
keyspace string) ([]*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
|
||||
tabletType := topodatapb.TabletType_RDONLY
|
||||
// allParts will collect the query-parts from all the shards. It's protected
|
||||
// by allPartsMutex.
|
||||
var allParts []*vtgatepb.SplitQueryResponse_Part
|
||||
var allPartsMutex sync.Mutex
|
||||
|
||||
allErrors := stc.multiGo(
|
||||
ctx,
|
||||
"SplitQuery",
|
||||
keyspace,
|
||||
shards,
|
||||
tabletType,
|
||||
NewSafeSession(&vtgatepb.Session{}),
|
||||
false,
|
||||
func(shard string, transactionID int64) error {
|
||||
// Get all splits from this shard
|
||||
querySplits, err := stc.gateway.SplitQueryV2(
|
||||
ctx,
|
||||
keyspace,
|
||||
shard,
|
||||
tabletType,
|
||||
sql,
|
||||
bindVariables,
|
||||
splitColumns,
|
||||
perShardSplitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parts := make([]*vtgatepb.SplitQueryResponse_Part, len(querySplits))
|
||||
for i, querySplit := range querySplits {
|
||||
parts[i], err = querySplitToQueryPartFunc(&querySplit, shard)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Aggregate the parts from this shard into allParts.
|
||||
allPartsMutex.Lock()
|
||||
defer allPartsMutex.Unlock()
|
||||
allParts = append(allParts, parts...)
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if allErrors.HasErrors() {
|
||||
err := allErrors.AggrError(stc.aggregateErrors)
|
||||
return nil, err
|
||||
}
|
||||
// We shuffle the query-parts here. External frameworks like MapReduce may
|
||||
// "deal" these jobs to workers in the order they are in the list. Without
|
||||
// shuffling workers can be very unevenly distributed among
|
||||
// the shards they query. E.g. all workers will first query the first shard,
|
||||
// then most of them to the second shard, etc, which results with uneven
|
||||
// load balancing among shards.
|
||||
shuffleQueryParts(allParts)
|
||||
return allParts, nil
|
||||
}
|
||||
|
||||
// randomGenerator is the randomGenerator used for the randomness
|
||||
// of 'shuffleQueryParts'. It's initialized in 'init()' below.
|
||||
type shuffleQueryPartsRandomGeneratorInterface interface {
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/youtube/vitess/go/vt/vtgate/txbuffer"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
querypb "github.com/youtube/vitess/go/vt/proto/query"
|
||||
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
|
||||
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
|
||||
)
|
||||
|
@ -169,7 +170,9 @@ func (sdc *ShardConn) Begin(ctx context.Context) (transactionID int64, err error
|
|||
err = sdc.withRetry(ctx, func(conn tabletconn.TabletConn) error {
|
||||
var innerErr error
|
||||
// Potentially buffer this transaction.
|
||||
txbuffer.FakeBuffer(sdc.keyspace, sdc.shard, attemptNumber)
|
||||
if bufferErr := txbuffer.FakeBuffer(sdc.keyspace, sdc.shard, attemptNumber); bufferErr != nil {
|
||||
return bufferErr
|
||||
}
|
||||
transactionID, innerErr = conn.Begin(ctx)
|
||||
attemptNumber++
|
||||
return innerErr
|
||||
|
@ -204,6 +207,28 @@ func (sdc *ShardConn) SplitQuery(ctx context.Context, sql string, bindVariables
|
|||
return
|
||||
}
|
||||
|
||||
// SplitQueryV2 splits a query into sub queries. The retry rules are the same as Execute.
|
||||
// TODO(erez): Rename to SplitQuery after the migration to SplitQuery V2 is done.
|
||||
func (sdc *ShardConn) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) (queries []querytypes.QuerySplit, err error) {
|
||||
|
||||
err = sdc.withRetry(ctx, func(conn tabletconn.TabletConn) error {
|
||||
var innerErr error
|
||||
queries, innerErr = conn.SplitQueryV2(ctx, querytypes.BoundQuery{
|
||||
Sql: sql,
|
||||
BindVariables: bindVariables,
|
||||
}, splitColumns, splitCount, numRowsPerQueryPart, algorithm)
|
||||
return innerErr
|
||||
}, 0, false)
|
||||
return
|
||||
}
|
||||
|
||||
// Close closes the underlying TabletConn.
|
||||
func (sdc *ShardConn) Close() {
|
||||
if sdc.ticker != nil {
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/youtube/vitess/go/vt/tabletserver/querytypes"
|
||||
"github.com/youtube/vitess/go/vt/topo"
|
||||
|
||||
querypb "github.com/youtube/vitess/go/vt/proto/query"
|
||||
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
|
||||
)
|
||||
|
||||
|
@ -140,6 +141,23 @@ func (sg *shardGateway) SplitQuery(ctx context.Context, keyspace string, shard s
|
|||
return sg.getConnection(ctx, keyspace, shard, tabletType).SplitQuery(ctx, sql, bindVars, splitColumn, splitCount)
|
||||
}
|
||||
|
||||
// SplitQuery splits a query into sub-queries for the specified keyspace, shard, and tablet type.
|
||||
// TODO(erez): Rename to SplitQuery after the migration to SplitQuery V2 is done.
|
||||
func (sg *shardGateway) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
keyspace string,
|
||||
shard string,
|
||||
tabletType topodatapb.TabletType,
|
||||
sql string,
|
||||
bindVars map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) ([]querytypes.QuerySplit, error) {
|
||||
return sg.getConnection(ctx, keyspace, shard, tabletType).SplitQueryV2(
|
||||
ctx, sql, bindVars, splitColumns, splitCount, numRowsPerQueryPart, algorithm)
|
||||
}
|
||||
|
||||
// Close shuts down the underlying connections.
|
||||
func (sg *shardGateway) Close(ctx context.Context) error {
|
||||
sg.mu.Lock()
|
||||
|
|
|
@ -15,11 +15,14 @@ but will not return transient errors during the buffering time.
|
|||
package txbuffer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/youtube/vitess/go/stats"
|
||||
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
|
||||
"github.com/youtube/vitess/go/vt/vterrors"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -39,26 +42,32 @@ var (
|
|||
// timeSleep can be mocked out in unit tests
|
||||
var timeSleep = time.Sleep
|
||||
|
||||
// errBufferFull is the error returned a buffer request is rejected because the buffer is full.
|
||||
var errBufferFull = vterrors.FromError(
|
||||
vtrpcpb.ErrorCode_TRANSIENT_ERROR,
|
||||
errors.New("transaction buffer full, rejecting request"),
|
||||
)
|
||||
|
||||
// FakeBuffer will pretend to buffer new transactions in VTGate.
|
||||
// Transactions *will NOT actually be buffered*, they will just have a delayed start time.
|
||||
// This can be useful to understand what the impact of trasaction buffering will be
|
||||
// on upstream callers. Once the impact is measured, it can be used to tweak parameter values
|
||||
// for the best behavior.
|
||||
// FakeBuffer should be called before the VtTablet Begin, otherwise it will increase transaction times.
|
||||
func FakeBuffer(keyspace, shard string, attemptNumber int) {
|
||||
func FakeBuffer(keyspace, shard string, attemptNumber int) error {
|
||||
// Only buffer on the first Begin attempt, not on possible retries.
|
||||
if !*enableFakeTxBuffer || attemptNumber != 0 {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
if keyspace != *bufferKeyspace || shard != *bufferShard {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
bufferedTransactionsAttempted.Add(1)
|
||||
|
||||
bufferMu.Lock()
|
||||
if int(bufferedTransactions.Get()) >= *maxBufferSize {
|
||||
bufferMu.Unlock()
|
||||
return
|
||||
return errBufferFull
|
||||
}
|
||||
bufferedTransactions.Add(1)
|
||||
bufferMu.Unlock()
|
||||
|
@ -67,4 +76,5 @@ func FakeBuffer(keyspace, shard string, attemptNumber int) {
|
|||
timeSleep(*fakeBufferDelay)
|
||||
// Don't need to lock for this, as there's no race when decrementing the count
|
||||
bufferedTransactions.Add(-1)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ func TestFakeBuffer(t *testing.T) {
|
|||
wantCalled bool
|
||||
// expected value of BufferedTransactionAttempts
|
||||
wantAttempted int
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
desc: "enableFakeBuffer=False",
|
||||
|
@ -88,6 +89,7 @@ func TestFakeBuffer(t *testing.T) {
|
|||
bufferedTransactions: *maxBufferSize,
|
||||
// When the buffer is full, bufferedTransactionsAttempted should still be incremented
|
||||
wantAttempted: 1,
|
||||
wantErr: errBufferFull,
|
||||
},
|
||||
{
|
||||
desc: "buffered successful",
|
||||
|
@ -107,7 +109,11 @@ func TestFakeBuffer(t *testing.T) {
|
|||
|
||||
*enableFakeTxBuffer = test.enableFakeBuffer
|
||||
|
||||
FakeBuffer(test.keyspace, test.shard, test.attemptNumber)
|
||||
gotErr := FakeBuffer(test.keyspace, test.shard, test.attemptNumber)
|
||||
|
||||
if gotErr != test.wantErr {
|
||||
t.Errorf("With %v, FakeBuffer() => %v; want: %v", test.desc, gotErr, test.wantErr)
|
||||
}
|
||||
|
||||
if controller.called != test.wantCalled {
|
||||
t.Errorf("With %v, FakeBuffer() => timeSleep.called: %v; want: %v",
|
||||
|
@ -162,23 +168,27 @@ func TestParallelFakeBuffer(t *testing.T) {
|
|||
|
||||
wg.Add(1)
|
||||
finished := make(chan struct{})
|
||||
var gotErr error
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
FakeBuffer(*bufferKeyspace, *bufferShard, 0)
|
||||
gotErr = FakeBuffer(*bufferKeyspace, *bufferShard, 0)
|
||||
close(finished)
|
||||
}()
|
||||
|
||||
if wantFakeSleepCalled {
|
||||
// the first maxBufferSize calls to FakeBuffer
|
||||
// should call sleep, wait until they do
|
||||
<-controller.blocked
|
||||
} else {
|
||||
// the rest should not block, wait until they're done
|
||||
<-finished
|
||||
// wait until either the gorouotine is blocked (because it's buffering) or until
|
||||
// it's finished (because it shouldn't be buffered).
|
||||
select {
|
||||
case <-controller.blocked:
|
||||
case <-finished:
|
||||
}
|
||||
|
||||
if controller.called {
|
||||
controllers = append(controllers, controller)
|
||||
} else {
|
||||
// if we didn't call fakeSleep, the buffer is full and should return an error saying so.
|
||||
if gotErr != errBufferFull {
|
||||
t.Errorf("On iteration %v, FakeBuffer() => %v; want: %v", i, gotErr, errBufferFull)
|
||||
}
|
||||
}
|
||||
|
||||
if controller.called != wantFakeSleepCalled {
|
||||
|
|
|
@ -6,7 +6,6 @@ package vindexes
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"sort"
|
||||
|
@ -16,7 +15,8 @@ import (
|
|||
// VSchema represents the denormalized version of VSchemaFormal,
|
||||
// used for building routing plans.
|
||||
type VSchema struct {
|
||||
Tables map[string]*Table
|
||||
tables map[string]*Table
|
||||
Keyspaces map[string]*KeyspaceSchema
|
||||
}
|
||||
|
||||
// Table represnts a table in VSchema.
|
||||
|
@ -45,6 +45,12 @@ type ColVindex struct {
|
|||
Vindex Vindex
|
||||
}
|
||||
|
||||
// KeyspaceSchema contains the schema(table) for a keyspace.
|
||||
type KeyspaceSchema struct {
|
||||
Keyspace *Keyspace
|
||||
Tables map[string]*Table
|
||||
}
|
||||
|
||||
// Autoinc contains the auto-inc information for a table.
|
||||
type Autoinc struct {
|
||||
Col string
|
||||
|
@ -56,61 +62,62 @@ type Autoinc struct {
|
|||
|
||||
// BuildVSchema builds a VSchema from a VSchemaFormal.
|
||||
func BuildVSchema(source *VSchemaFormal) (vschema *VSchema, err error) {
|
||||
vschema = &VSchema{Tables: make(map[string]*Table)}
|
||||
keyspaces := buildKeyspaces(source)
|
||||
vschema = &VSchema{
|
||||
tables: make(map[string]*Table),
|
||||
Keyspaces: make(map[string]*KeyspaceSchema),
|
||||
}
|
||||
buildKeyspaces(source, vschema)
|
||||
// We have to build the sequences first to avoid
|
||||
// forward reference errors.
|
||||
err = buildSequences(source, vschema, keyspaces)
|
||||
err = buildSequences(source, vschema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = buildTables(source, vschema, keyspaces)
|
||||
err = buildTables(source, vschema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return vschema, nil
|
||||
}
|
||||
|
||||
func buildKeyspaces(source *VSchemaFormal) map[string]*Keyspace {
|
||||
k := make(map[string]*Keyspace)
|
||||
func buildKeyspaces(source *VSchemaFormal, vschema *VSchema) {
|
||||
for ksname, ks := range source.Keyspaces {
|
||||
k[ksname] = &Keyspace{
|
||||
Name: ksname,
|
||||
Sharded: ks.Sharded,
|
||||
vschema.Keyspaces[ksname] = &KeyspaceSchema{
|
||||
Keyspace: &Keyspace{
|
||||
Name: ksname,
|
||||
Sharded: ks.Sharded,
|
||||
},
|
||||
Tables: make(map[string]*Table),
|
||||
}
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
func buildSequences(source *VSchemaFormal, vschema *VSchema, keyspaces map[string]*Keyspace) error {
|
||||
func buildSequences(source *VSchemaFormal, vschema *VSchema) error {
|
||||
for ksname, ks := range source.Keyspaces {
|
||||
keyspace := keyspaces[ksname]
|
||||
for tname, cname := range ks.Tables {
|
||||
if _, ok := vschema.Tables[tname]; ok {
|
||||
return fmt.Errorf("table %s has multiple definitions", tname)
|
||||
}
|
||||
if cname == "" {
|
||||
keyspace := vschema.Keyspaces[ksname].Keyspace
|
||||
for tname, table := range ks.Tables {
|
||||
if table.Type != "Sequence" {
|
||||
continue
|
||||
}
|
||||
class, ok := ks.Classes[cname]
|
||||
if !ok {
|
||||
return fmt.Errorf("class %s not found for table %s", cname, tname)
|
||||
t := &Table{
|
||||
Name: tname,
|
||||
Keyspace: keyspace,
|
||||
IsSequence: true,
|
||||
}
|
||||
if class.Type == "Sequence" {
|
||||
vschema.Tables[tname] = &Table{
|
||||
Name: tname,
|
||||
Keyspace: keyspace,
|
||||
IsSequence: true,
|
||||
}
|
||||
if _, ok := vschema.tables[tname]; ok {
|
||||
vschema.tables[tname] = nil
|
||||
} else {
|
||||
vschema.tables[tname] = t
|
||||
}
|
||||
vschema.Keyspaces[ksname].Tables[tname] = t
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildTables(source *VSchemaFormal, vschema *VSchema, keyspaces map[string]*Keyspace) error {
|
||||
func buildTables(source *VSchemaFormal, vschema *VSchema) error {
|
||||
for ksname, ks := range source.Keyspaces {
|
||||
keyspace := keyspaces[ksname]
|
||||
keyspace := vschema.Keyspaces[ksname].Keyspace
|
||||
vindexes := make(map[string]Vindex)
|
||||
for vname, vindexInfo := range ks.Vindexes {
|
||||
vindex, err := CreateVindex(vindexInfo.Type, vname, vindexInfo.Params)
|
||||
|
@ -125,29 +132,27 @@ func buildTables(source *VSchemaFormal, vschema *VSchema, keyspaces map[string]*
|
|||
}
|
||||
vindexes[vname] = vindex
|
||||
}
|
||||
for tname, cname := range ks.Tables {
|
||||
if t, ok := vschema.Tables[tname]; ok {
|
||||
if t.IsSequence {
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("table %s has multiple definitions", tname)
|
||||
for tname, table := range ks.Tables {
|
||||
if table.Type == "Sequence" {
|
||||
continue
|
||||
}
|
||||
t := &Table{
|
||||
Name: tname,
|
||||
Keyspace: keyspace,
|
||||
}
|
||||
if _, ok := vschema.tables[tname]; ok {
|
||||
vschema.tables[tname] = nil
|
||||
} else {
|
||||
vschema.tables[tname] = t
|
||||
}
|
||||
vschema.Keyspaces[ksname].Tables[tname] = t
|
||||
if !keyspace.Sharded {
|
||||
vschema.Tables[tname] = t
|
||||
continue
|
||||
}
|
||||
class, ok := ks.Classes[cname]
|
||||
if !ok {
|
||||
return fmt.Errorf("class %s not found for table %s", cname, tname)
|
||||
}
|
||||
for i, ind := range class.ColVindexes {
|
||||
for i, ind := range table.ColVindexes {
|
||||
vindexInfo, ok := ks.Vindexes[ind.Name]
|
||||
if !ok {
|
||||
return fmt.Errorf("vindex %s not found for class %s", ind.Name, cname)
|
||||
return fmt.Errorf("vindex %s not found for table %s", ind.Name, tname)
|
||||
}
|
||||
vindex := vindexes[ind.Name]
|
||||
owned := false
|
||||
|
@ -164,10 +169,10 @@ func buildTables(source *VSchemaFormal, vschema *VSchema, keyspaces map[string]*
|
|||
if i == 0 {
|
||||
// Perform Primary vindex check.
|
||||
if _, ok := columnVindex.Vindex.(Unique); !ok {
|
||||
return fmt.Errorf("primary vindex %s is not Unique for class %s", ind.Name, cname)
|
||||
return fmt.Errorf("primary vindex %s is not Unique for table %s", ind.Name, tname)
|
||||
}
|
||||
if owned {
|
||||
return fmt.Errorf("primary vindex %s cannot be owned for class %s", ind.Name, cname)
|
||||
return fmt.Errorf("primary vindex %s cannot be owned for table %s", ind.Name, tname)
|
||||
}
|
||||
}
|
||||
t.ColVindexes = append(t.ColVindexes, columnVindex)
|
||||
|
@ -176,11 +181,11 @@ func buildTables(source *VSchemaFormal, vschema *VSchema, keyspaces map[string]*
|
|||
}
|
||||
}
|
||||
t.Ordered = colVindexSorted(t.ColVindexes)
|
||||
if class.Autoinc != nil {
|
||||
t.Autoinc = &Autoinc{Col: class.Autoinc.Col, ColVindexNum: -1}
|
||||
seq, ok := vschema.Tables[class.Autoinc.Sequence]
|
||||
if table.Autoinc != nil {
|
||||
t.Autoinc = &Autoinc{Col: table.Autoinc.Col, ColVindexNum: -1}
|
||||
seq, ok := vschema.tables[table.Autoinc.Sequence]
|
||||
if !ok {
|
||||
return fmt.Errorf("sequence %s not found for class %s", class.Autoinc.Sequence, cname)
|
||||
return fmt.Errorf("sequence %s not found for table %s", table.Autoinc.Sequence, tname)
|
||||
}
|
||||
t.Autoinc.Sequence = seq
|
||||
for i, cv := range t.ColVindexes {
|
||||
|
@ -190,21 +195,48 @@ func buildTables(source *VSchemaFormal, vschema *VSchema, keyspaces map[string]*
|
|||
}
|
||||
}
|
||||
}
|
||||
vschema.Tables[tname] = t
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindTable returns a pointer to the Table if found.
|
||||
// Otherwise, it returns a reason, which is equivalent to an error.
|
||||
func (vschema *VSchema) FindTable(tablename string) (table *Table, err error) {
|
||||
if tablename == "" {
|
||||
return nil, errors.New("unsupported: compex table expression in DML")
|
||||
// Find returns a pointer to the Table. If a keyspace is specified, only tables
|
||||
// from that keyspace are searched. If the specified keyspace is unsharded
|
||||
// and no tables matched, it's considered valid: Find will construct a table
|
||||
// of that name and return it. If no kesypace is specified, then a table is returned
|
||||
// only if its name is unique across all keyspaces. If there is only one
|
||||
// keyspace in the vschema, and it's unsharded, then all table requests are considered
|
||||
// valid and belonging to that keyspace.
|
||||
func (vschema *VSchema) Find(keyspace, tablename string) (table *Table, err error) {
|
||||
if keyspace == "" {
|
||||
table, ok := vschema.tables[tablename]
|
||||
if table == nil {
|
||||
if ok {
|
||||
return nil, fmt.Errorf("ambiguous table reference: %s", tablename)
|
||||
}
|
||||
if len(vschema.Keyspaces) != 1 {
|
||||
return nil, fmt.Errorf("table %s not found", tablename)
|
||||
}
|
||||
// Loop happens only once.
|
||||
for _, ks := range vschema.Keyspaces {
|
||||
if ks.Keyspace.Sharded {
|
||||
return nil, fmt.Errorf("table %s not found", tablename)
|
||||
}
|
||||
return &Table{Name: tablename, Keyspace: ks.Keyspace}, nil
|
||||
}
|
||||
}
|
||||
return table, nil
|
||||
}
|
||||
table = vschema.Tables[tablename]
|
||||
ks, ok := vschema.Keyspaces[keyspace]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("keyspace %s not found in vschema", keyspace)
|
||||
}
|
||||
table = ks.Tables[tablename]
|
||||
if table == nil {
|
||||
return nil, fmt.Errorf("table %s not found", tablename)
|
||||
if ks.Keyspace.Sharded {
|
||||
return nil, fmt.Errorf("table %s not found", tablename)
|
||||
}
|
||||
return &Table{Name: tablename, Keyspace: ks.Keyspace}, nil
|
||||
}
|
||||
return table, nil
|
||||
}
|
||||
|
@ -236,8 +268,7 @@ type VSchemaFormal struct {
|
|||
type KeyspaceFormal struct {
|
||||
Sharded bool
|
||||
Vindexes map[string]VindexFormal
|
||||
Classes map[string]ClassFormal
|
||||
Tables map[string]string
|
||||
Tables map[string]TableFormal
|
||||
}
|
||||
|
||||
// VindexFormal is the info for each index as loaded from
|
||||
|
@ -248,9 +279,9 @@ type VindexFormal struct {
|
|||
Owner string
|
||||
}
|
||||
|
||||
// ClassFormal is the info for each table class as loaded from
|
||||
// TableFormal is the info for each table as loaded from
|
||||
// the source.
|
||||
type ClassFormal struct {
|
||||
type TableFormal struct {
|
||||
Type string
|
||||
ColVindexes []ColVindexFormal
|
||||
Autoinc *AutoincFormal
|
||||
|
|
|
@ -85,8 +85,8 @@ func TestUnshardedVSchema(t *testing.T) {
|
|||
good := VSchemaFormal{
|
||||
Keyspaces: map[string]KeyspaceFormal{
|
||||
"unsharded": {
|
||||
Tables: map[string]string{
|
||||
"t1": "",
|
||||
Tables: map[string]TableFormal{
|
||||
"t1": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -95,14 +95,23 @@ func TestUnshardedVSchema(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
ks := &Keyspace{
|
||||
Name: "unsharded",
|
||||
}
|
||||
t1 := &Table{
|
||||
Name: "t1",
|
||||
Keyspace: ks,
|
||||
}
|
||||
want := &VSchema{
|
||||
Tables: map[string]*Table{
|
||||
"t1": {
|
||||
Name: "t1",
|
||||
Keyspace: &Keyspace{
|
||||
Name: "unsharded",
|
||||
tables: map[string]*Table{
|
||||
"t1": t1,
|
||||
},
|
||||
Keyspaces: map[string]*KeyspaceSchema{
|
||||
"unsharded": {
|
||||
Keyspace: ks,
|
||||
Tables: map[string]*Table{
|
||||
"t1": t1,
|
||||
},
|
||||
ColVindexes: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -129,7 +138,7 @@ func TestShardedVSchemaOwned(t *testing.T) {
|
|||
Owner: "t1",
|
||||
},
|
||||
},
|
||||
Classes: map[string]ClassFormal{
|
||||
Tables: map[string]TableFormal{
|
||||
"t1": {
|
||||
ColVindexes: []ColVindexFormal{
|
||||
{
|
||||
|
@ -142,9 +151,6 @@ func TestShardedVSchemaOwned(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
Tables: map[string]string{
|
||||
"t1": "t1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -152,42 +158,52 @@ func TestShardedVSchemaOwned(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
want := &VSchema{
|
||||
Tables: map[string]*Table{
|
||||
"t1": {
|
||||
Name: "t1",
|
||||
Keyspace: &Keyspace{
|
||||
Name: "sharded",
|
||||
Sharded: true,
|
||||
ks := &Keyspace{
|
||||
Name: "sharded",
|
||||
Sharded: true,
|
||||
}
|
||||
t1 := &Table{
|
||||
Name: "t1",
|
||||
Keyspace: ks,
|
||||
ColVindexes: []*ColVindex{
|
||||
{
|
||||
Col: "c1",
|
||||
Type: "stfu",
|
||||
Name: "stfu1",
|
||||
Vindex: &stFU{
|
||||
name: "stfu1",
|
||||
Params: map[string]interface{}{
|
||||
"stfu1": 1,
|
||||
},
|
||||
},
|
||||
ColVindexes: []*ColVindex{
|
||||
{
|
||||
Col: "c1",
|
||||
Type: "stfu",
|
||||
Name: "stfu1",
|
||||
Vindex: &stFU{
|
||||
name: "stfu1",
|
||||
Params: map[string]interface{}{
|
||||
"stfu1": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Col: "c2",
|
||||
Type: "stln",
|
||||
Name: "stln1",
|
||||
Owned: true,
|
||||
Vindex: &stLN{name: "stln1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Col: "c2",
|
||||
Type: "stln",
|
||||
Name: "stln1",
|
||||
Owned: true,
|
||||
Vindex: &stLN{name: "stln1"},
|
||||
},
|
||||
},
|
||||
}
|
||||
t1.Ordered = []*ColVindex{
|
||||
t1.ColVindexes[1],
|
||||
t1.ColVindexes[0],
|
||||
}
|
||||
t1.Owned = t1.ColVindexes[1:]
|
||||
want := &VSchema{
|
||||
tables: map[string]*Table{
|
||||
"t1": t1,
|
||||
},
|
||||
Keyspaces: map[string]*KeyspaceSchema{
|
||||
"sharded": {
|
||||
Keyspace: ks,
|
||||
Tables: map[string]*Table{
|
||||
"t1": t1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
want.Tables["t1"].Ordered = []*ColVindex{
|
||||
want.Tables["t1"].ColVindexes[1],
|
||||
want.Tables["t1"].ColVindexes[0],
|
||||
}
|
||||
want.Tables["t1"].Owned = want.Tables["t1"].ColVindexes[1:]
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
gotjson, _ := json.Marshal(got)
|
||||
wantjson, _ := json.Marshal(want)
|
||||
|
@ -210,7 +226,7 @@ func TestShardedVSchemaNotOwned(t *testing.T) {
|
|||
Owner: "",
|
||||
},
|
||||
},
|
||||
Classes: map[string]ClassFormal{
|
||||
Tables: map[string]TableFormal{
|
||||
"t1": {
|
||||
ColVindexes: []ColVindexFormal{
|
||||
{
|
||||
|
@ -223,9 +239,6 @@ func TestShardedVSchemaNotOwned(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
Tables: map[string]string{
|
||||
"t1": "t1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -233,36 +246,46 @@ func TestShardedVSchemaNotOwned(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
want := &VSchema{
|
||||
Tables: map[string]*Table{
|
||||
"t1": {
|
||||
Name: "t1",
|
||||
Keyspace: &Keyspace{
|
||||
Name: "sharded",
|
||||
Sharded: true,
|
||||
},
|
||||
ColVindexes: []*ColVindex{
|
||||
{
|
||||
Col: "c1",
|
||||
Type: "stlu",
|
||||
Name: "stlu1",
|
||||
Owned: false,
|
||||
Vindex: &stLU{name: "stlu1"},
|
||||
},
|
||||
{
|
||||
Col: "c2",
|
||||
Type: "stfu",
|
||||
Name: "stfu1",
|
||||
Owned: false,
|
||||
Vindex: &stFU{name: "stfu1"},
|
||||
},
|
||||
},
|
||||
ks := &Keyspace{
|
||||
Name: "sharded",
|
||||
Sharded: true,
|
||||
}
|
||||
t1 := &Table{
|
||||
Name: "t1",
|
||||
Keyspace: ks,
|
||||
ColVindexes: []*ColVindex{
|
||||
{
|
||||
Col: "c1",
|
||||
Type: "stlu",
|
||||
Name: "stlu1",
|
||||
Owned: false,
|
||||
Vindex: &stLU{name: "stlu1"},
|
||||
},
|
||||
{
|
||||
Col: "c2",
|
||||
Type: "stfu",
|
||||
Name: "stfu1",
|
||||
Owned: false,
|
||||
Vindex: &stFU{name: "stfu1"},
|
||||
},
|
||||
},
|
||||
}
|
||||
want.Tables["t1"].Ordered = []*ColVindex{
|
||||
want.Tables["t1"].ColVindexes[1],
|
||||
want.Tables["t1"].ColVindexes[0],
|
||||
t1.Ordered = []*ColVindex{
|
||||
t1.ColVindexes[1],
|
||||
t1.ColVindexes[0],
|
||||
}
|
||||
want := &VSchema{
|
||||
tables: map[string]*Table{
|
||||
"t1": t1,
|
||||
},
|
||||
Keyspaces: map[string]*KeyspaceSchema{
|
||||
"sharded": {
|
||||
Keyspace: ks,
|
||||
Tables: map[string]*Table{
|
||||
"t1": t1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("BuildVSchema:s\n%v, want\n%v", got, want)
|
||||
|
@ -283,39 +306,6 @@ func TestLoadVSchemaFail(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestBuildVSchemaClassNotFoundFail(t *testing.T) {
|
||||
bad := VSchemaFormal{
|
||||
Keyspaces: map[string]KeyspaceFormal{
|
||||
"sharded": {
|
||||
Sharded: true,
|
||||
Vindexes: map[string]VindexFormal{
|
||||
"stfu": {
|
||||
Type: "stfu",
|
||||
},
|
||||
},
|
||||
Classes: map[string]ClassFormal{
|
||||
"notexist": {
|
||||
ColVindexes: []ColVindexFormal{
|
||||
{
|
||||
Col: "c1",
|
||||
Name: "noexist",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Tables: map[string]string{
|
||||
"t1": "t1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := BuildVSchema(&bad)
|
||||
want := "class t1 not found for table t1"
|
||||
if err == nil || err.Error() != want {
|
||||
t.Errorf("BuildVSchema: %v, want %v", err, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildVSchemaVindexNotFoundFail(t *testing.T) {
|
||||
bad := VSchemaFormal{
|
||||
Keyspaces: map[string]KeyspaceFormal{
|
||||
|
@ -326,7 +316,7 @@ func TestBuildVSchemaVindexNotFoundFail(t *testing.T) {
|
|||
Type: "noexist",
|
||||
},
|
||||
},
|
||||
Classes: map[string]ClassFormal{
|
||||
Tables: map[string]TableFormal{
|
||||
"t1": {
|
||||
ColVindexes: []ColVindexFormal{
|
||||
{
|
||||
|
@ -336,9 +326,6 @@ func TestBuildVSchemaVindexNotFoundFail(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
Tables: map[string]string{
|
||||
"t1": "t1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -359,7 +346,7 @@ func TestBuildVSchemaInvalidVindexFail(t *testing.T) {
|
|||
Type: "stf",
|
||||
},
|
||||
},
|
||||
Classes: map[string]ClassFormal{
|
||||
Tables: map[string]TableFormal{
|
||||
"t1": {
|
||||
ColVindexes: []ColVindexFormal{
|
||||
{
|
||||
|
@ -369,9 +356,6 @@ func TestBuildVSchemaInvalidVindexFail(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
Tables: map[string]string{
|
||||
"t1": "t1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -382,57 +366,121 @@ func TestBuildVSchemaInvalidVindexFail(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestBuildVSchemaDupTableFail(t *testing.T) {
|
||||
bad := VSchemaFormal{
|
||||
func TestBuildVSchemaDupSeq(t *testing.T) {
|
||||
good := VSchemaFormal{
|
||||
Keyspaces: map[string]KeyspaceFormal{
|
||||
"sharded": {
|
||||
Sharded: true,
|
||||
Vindexes: map[string]VindexFormal{
|
||||
"stfu": {
|
||||
Type: "stfu",
|
||||
},
|
||||
},
|
||||
Classes: map[string]ClassFormal{
|
||||
"ksa": {
|
||||
Tables: map[string]TableFormal{
|
||||
"t1": {
|
||||
ColVindexes: []ColVindexFormal{
|
||||
{
|
||||
Col: "c1",
|
||||
Name: "stfu",
|
||||
},
|
||||
},
|
||||
Type: "Sequence",
|
||||
},
|
||||
},
|
||||
Tables: map[string]string{
|
||||
"t1": "t1",
|
||||
},
|
||||
},
|
||||
"sharded1": {
|
||||
Sharded: true,
|
||||
Vindexes: map[string]VindexFormal{
|
||||
"stfu": {
|
||||
Type: "stfu",
|
||||
},
|
||||
},
|
||||
Classes: map[string]ClassFormal{
|
||||
"ksb": {
|
||||
Tables: map[string]TableFormal{
|
||||
"t1": {
|
||||
ColVindexes: []ColVindexFormal{
|
||||
{
|
||||
Col: "c1",
|
||||
Name: "stfu",
|
||||
},
|
||||
},
|
||||
Type: "Sequence",
|
||||
},
|
||||
},
|
||||
Tables: map[string]string{
|
||||
"t1": "t1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := BuildVSchema(&bad)
|
||||
want := "table t1 has multiple definitions"
|
||||
if err == nil || err.Error() != want {
|
||||
t.Errorf("BuildVSchema: %v, want %v", err, want)
|
||||
ksa := &Keyspace{
|
||||
Name: "ksa",
|
||||
}
|
||||
ksb := &Keyspace{
|
||||
Name: "ksb",
|
||||
}
|
||||
got, _ := BuildVSchema(&good)
|
||||
t1a := &Table{
|
||||
Name: "t1",
|
||||
Keyspace: ksa,
|
||||
IsSequence: true,
|
||||
}
|
||||
t1b := &Table{
|
||||
Name: "t1",
|
||||
Keyspace: ksb,
|
||||
IsSequence: true,
|
||||
}
|
||||
want := &VSchema{
|
||||
tables: map[string]*Table{
|
||||
"t1": nil,
|
||||
},
|
||||
Keyspaces: map[string]*KeyspaceSchema{
|
||||
"ksa": {
|
||||
Keyspace: ksa,
|
||||
Tables: map[string]*Table{
|
||||
"t1": t1a,
|
||||
},
|
||||
},
|
||||
"ksb": {
|
||||
Keyspace: ksb,
|
||||
Tables: map[string]*Table{
|
||||
"t1": t1b,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
gotjson, _ := json.Marshal(got)
|
||||
wantjson, _ := json.Marshal(want)
|
||||
t.Errorf("BuildVSchema:s\n%s, want\n%s", gotjson, wantjson)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildVSchemaDupTable(t *testing.T) {
|
||||
good := VSchemaFormal{
|
||||
Keyspaces: map[string]KeyspaceFormal{
|
||||
"ksa": {
|
||||
Tables: map[string]TableFormal{
|
||||
"t1": {},
|
||||
},
|
||||
},
|
||||
"ksb": {
|
||||
Tables: map[string]TableFormal{
|
||||
"t1": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
got, _ := BuildVSchema(&good)
|
||||
ksa := &Keyspace{
|
||||
Name: "ksa",
|
||||
}
|
||||
t1a := &Table{
|
||||
Name: "t1",
|
||||
Keyspace: ksa,
|
||||
}
|
||||
ksb := &Keyspace{
|
||||
Name: "ksb",
|
||||
}
|
||||
t1b := &Table{
|
||||
Name: "t1",
|
||||
Keyspace: ksb,
|
||||
}
|
||||
want := &VSchema{
|
||||
tables: map[string]*Table{
|
||||
"t1": nil,
|
||||
},
|
||||
Keyspaces: map[string]*KeyspaceSchema{
|
||||
"ksa": {
|
||||
Keyspace: ksa,
|
||||
Tables: map[string]*Table{
|
||||
"t1": t1a,
|
||||
},
|
||||
},
|
||||
"ksb": {
|
||||
Keyspace: ksb,
|
||||
Tables: map[string]*Table{
|
||||
"t1": t1b,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
gotjson, _ := json.Marshal(got)
|
||||
wantjson, _ := json.Marshal(want)
|
||||
t.Errorf("BuildVSchema:s\n%s, want\n%s", gotjson, wantjson)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -446,7 +494,7 @@ func TestBuildVSchemaNoindexFail(t *testing.T) {
|
|||
Type: "stfu",
|
||||
},
|
||||
},
|
||||
Classes: map[string]ClassFormal{
|
||||
Tables: map[string]TableFormal{
|
||||
"t1": {
|
||||
ColVindexes: []ColVindexFormal{
|
||||
{
|
||||
|
@ -456,14 +504,11 @@ func TestBuildVSchemaNoindexFail(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
Tables: map[string]string{
|
||||
"t1": "t1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := BuildVSchema(&bad)
|
||||
want := "vindex notexist not found for class t1"
|
||||
want := "vindex notexist not found for table t1"
|
||||
if err == nil || err.Error() != want {
|
||||
t.Errorf("BuildVSchema: %v, want %v", err, want)
|
||||
}
|
||||
|
@ -479,7 +524,7 @@ func TestBuildVSchemaNotUniqueFail(t *testing.T) {
|
|||
Type: "stln",
|
||||
},
|
||||
},
|
||||
Classes: map[string]ClassFormal{
|
||||
Tables: map[string]TableFormal{
|
||||
"t1": {
|
||||
ColVindexes: []ColVindexFormal{
|
||||
{
|
||||
|
@ -489,14 +534,11 @@ func TestBuildVSchemaNotUniqueFail(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
Tables: map[string]string{
|
||||
"t1": "t1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := BuildVSchema(&bad)
|
||||
want := "primary vindex stln is not Unique for class t1"
|
||||
want := "primary vindex stln is not Unique for table t1"
|
||||
if err == nil || err.Error() != want {
|
||||
t.Errorf("BuildVSchema: %v, want %v", err, want)
|
||||
}
|
||||
|
@ -513,7 +555,7 @@ func TestBuildVSchemaPrimaryNonFunctionalFail(t *testing.T) {
|
|||
Owner: "t1",
|
||||
},
|
||||
},
|
||||
Classes: map[string]ClassFormal{
|
||||
Tables: map[string]TableFormal{
|
||||
"t1": {
|
||||
ColVindexes: []ColVindexFormal{
|
||||
{
|
||||
|
@ -523,14 +565,11 @@ func TestBuildVSchemaPrimaryNonFunctionalFail(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
Tables: map[string]string{
|
||||
"t1": "t1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := BuildVSchema(&bad)
|
||||
want := "primary vindex stlu cannot be owned for class t1"
|
||||
want := "primary vindex stlu cannot be owned for table t1"
|
||||
if err == nil || err.Error() != want {
|
||||
t.Errorf("BuildVSchema: %v, want %v", err, want)
|
||||
}
|
||||
|
@ -540,14 +579,11 @@ func TestSequence(t *testing.T) {
|
|||
good := VSchemaFormal{
|
||||
Keyspaces: map[string]KeyspaceFormal{
|
||||
"unsharded": {
|
||||
Classes: map[string]ClassFormal{
|
||||
Tables: map[string]TableFormal{
|
||||
"seq": {
|
||||
Type: "Sequence",
|
||||
},
|
||||
},
|
||||
Tables: map[string]string{
|
||||
"seq": "seq",
|
||||
},
|
||||
},
|
||||
"sharded": {
|
||||
Sharded: true,
|
||||
|
@ -559,7 +595,7 @@ func TestSequence(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
Classes: map[string]ClassFormal{
|
||||
Tables: map[string]TableFormal{
|
||||
"t1": {
|
||||
ColVindexes: []ColVindexFormal{
|
||||
{
|
||||
|
@ -573,9 +609,6 @@ func TestSequence(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
Tables: map[string]string{
|
||||
"t1": "t1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -583,44 +616,61 @@ func TestSequence(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
ksu := &Keyspace{
|
||||
Name: "unsharded",
|
||||
}
|
||||
kss := &Keyspace{
|
||||
Name: "sharded",
|
||||
Sharded: true,
|
||||
}
|
||||
seq := &Table{
|
||||
Name: "seq",
|
||||
Keyspace: &Keyspace{
|
||||
Name: "unsharded",
|
||||
},
|
||||
Name: "seq",
|
||||
Keyspace: ksu,
|
||||
IsSequence: true,
|
||||
}
|
||||
want := &VSchema{
|
||||
Tables: map[string]*Table{
|
||||
"seq": seq,
|
||||
"t1": {
|
||||
Name: "t1",
|
||||
Keyspace: &Keyspace{
|
||||
Name: "sharded",
|
||||
Sharded: true,
|
||||
},
|
||||
ColVindexes: []*ColVindex{
|
||||
{
|
||||
Col: "c1",
|
||||
Type: "stfu",
|
||||
Name: "stfu1",
|
||||
Vindex: &stFU{
|
||||
name: "stfu1",
|
||||
Params: map[string]interface{}{
|
||||
"stfu1": 1,
|
||||
},
|
||||
},
|
||||
t1 := &Table{
|
||||
Name: "t1",
|
||||
Keyspace: kss,
|
||||
ColVindexes: []*ColVindex{
|
||||
{
|
||||
Col: "c1",
|
||||
Type: "stfu",
|
||||
Name: "stfu1",
|
||||
Vindex: &stFU{
|
||||
name: "stfu1",
|
||||
Params: map[string]interface{}{
|
||||
"stfu1": 1,
|
||||
},
|
||||
},
|
||||
Autoinc: &Autoinc{
|
||||
Col: "c1",
|
||||
Sequence: seq,
|
||||
},
|
||||
},
|
||||
},
|
||||
Autoinc: &Autoinc{
|
||||
Col: "c1",
|
||||
Sequence: seq,
|
||||
},
|
||||
}
|
||||
want.Tables["t1"].Ordered = []*ColVindex{
|
||||
want.Tables["t1"].ColVindexes[0],
|
||||
t1.Ordered = []*ColVindex{
|
||||
t1.ColVindexes[0],
|
||||
}
|
||||
want := &VSchema{
|
||||
tables: map[string]*Table{
|
||||
"seq": seq,
|
||||
"t1": t1,
|
||||
},
|
||||
Keyspaces: map[string]*KeyspaceSchema{
|
||||
"unsharded": {
|
||||
Keyspace: ksu,
|
||||
Tables: map[string]*Table{
|
||||
"seq": seq,
|
||||
},
|
||||
},
|
||||
"sharded": {
|
||||
Keyspace: kss,
|
||||
Tables: map[string]*Table{
|
||||
"t1": t1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
gotjson, _ := json.Marshal(got)
|
||||
|
@ -630,11 +680,11 @@ func TestSequence(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBadSequence(t *testing.T) {
|
||||
good := VSchemaFormal{
|
||||
bad := VSchemaFormal{
|
||||
Keyspaces: map[string]KeyspaceFormal{
|
||||
"sharded": {
|
||||
Sharded: true,
|
||||
Classes: map[string]ClassFormal{
|
||||
Tables: map[string]TableFormal{
|
||||
"t1": {
|
||||
Autoinc: &AutoincFormal{
|
||||
Col: "c1",
|
||||
|
@ -642,15 +692,122 @@ func TestBadSequence(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
Tables: map[string]string{
|
||||
"t1": "t1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := BuildVSchema(&good)
|
||||
want := "sequence seq not found for class t1"
|
||||
_, err := BuildVSchema(&bad)
|
||||
want := "sequence seq not found for table t1"
|
||||
if err == nil || err.Error() != want {
|
||||
t.Errorf("BuildVSchema: %v, want %v", err, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFind(t *testing.T) {
|
||||
input := VSchemaFormal{
|
||||
Keyspaces: map[string]KeyspaceFormal{
|
||||
"ksa": {
|
||||
Tables: map[string]TableFormal{
|
||||
"ta": {},
|
||||
"t1": {},
|
||||
},
|
||||
},
|
||||
"ksb": {
|
||||
Sharded: true,
|
||||
Tables: map[string]TableFormal{
|
||||
"tb": {},
|
||||
"t1": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
vschema, _ := BuildVSchema(&input)
|
||||
_, err := vschema.Find("", "t1")
|
||||
wantErr := "ambiguous table reference: t1"
|
||||
if err == nil || err.Error() != wantErr {
|
||||
t.Errorf("Find(\"\"): %v, want %s", err, wantErr)
|
||||
}
|
||||
_, err = vschema.Find("", "none")
|
||||
wantErr = "table none not found"
|
||||
if err == nil || err.Error() != wantErr {
|
||||
t.Errorf("Find(\"\"): %v, want %s", err, wantErr)
|
||||
}
|
||||
got, err := vschema.Find("", "ta")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
ta := &Table{
|
||||
Name: "ta",
|
||||
Keyspace: &Keyspace{
|
||||
Name: "ksa",
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(got, ta) {
|
||||
t.Errorf("Find(\"t1a\"): %+v, want %+v", got, ta)
|
||||
}
|
||||
got, err = vschema.Find("ksa", "ta")
|
||||
if !reflect.DeepEqual(got, ta) {
|
||||
t.Errorf("Find(\"t1a\"): %+v, want %+v", got, ta)
|
||||
}
|
||||
none := &Table{
|
||||
Name: "none",
|
||||
Keyspace: &Keyspace{
|
||||
Name: "ksa",
|
||||
},
|
||||
}
|
||||
got, err = vschema.Find("ksa", "none")
|
||||
if !reflect.DeepEqual(got, none) {
|
||||
t.Errorf("Find(\"t1a\"): %+v, want %+v", got, none)
|
||||
}
|
||||
_, err = vschema.Find("ksb", "none")
|
||||
wantErr = "table none not found"
|
||||
if err == nil || err.Error() != wantErr {
|
||||
t.Errorf("Find(\"\"): %v, want %s", err, wantErr)
|
||||
}
|
||||
_, err = vschema.Find("none", "aa")
|
||||
wantErr = "keyspace none not found in vschema"
|
||||
if err == nil || err.Error() != wantErr {
|
||||
t.Errorf("Find(\"\"): %v, want %s", err, wantErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindSingleKeyspace(t *testing.T) {
|
||||
input := VSchemaFormal{
|
||||
Keyspaces: map[string]KeyspaceFormal{
|
||||
"ksa": {
|
||||
Tables: map[string]TableFormal{
|
||||
"ta": {},
|
||||
"t1": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
vschema, _ := BuildVSchema(&input)
|
||||
none := &Table{
|
||||
Name: "none",
|
||||
Keyspace: &Keyspace{
|
||||
Name: "ksa",
|
||||
},
|
||||
}
|
||||
got, _ := vschema.Find("", "none")
|
||||
if !reflect.DeepEqual(got, none) {
|
||||
t.Errorf("Find(\"t1a\"): %+v, want %+v", got, none)
|
||||
}
|
||||
input = VSchemaFormal{
|
||||
Keyspaces: map[string]KeyspaceFormal{
|
||||
"ksb": {
|
||||
Sharded: true,
|
||||
Tables: map[string]TableFormal{
|
||||
"tb": {},
|
||||
"t1": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
vschema, _ = BuildVSchema(&input)
|
||||
_, err := vschema.Find("", "none")
|
||||
wantErr := "table none not found"
|
||||
if err == nil || err.Error() != wantErr {
|
||||
t.Errorf("Find(\"\"): %v, want %s", err, wantErr)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/youtube/vitess/go/vt/logutil"
|
||||
"github.com/youtube/vitess/go/vt/servenv"
|
||||
"github.com/youtube/vitess/go/vt/sqlannotation"
|
||||
"github.com/youtube/vitess/go/vt/tabletserver/querytypes"
|
||||
"github.com/youtube/vitess/go/vt/tabletserver/tabletconn"
|
||||
"github.com/youtube/vitess/go/vt/topo"
|
||||
"github.com/youtube/vitess/go/vt/vterrors"
|
||||
|
@ -31,6 +32,7 @@ import (
|
|||
"github.com/youtube/vitess/go/vt/vtgate/vindexes"
|
||||
"github.com/youtube/vitess/go/vt/vtgate/vtgateservice"
|
||||
|
||||
querypb "github.com/youtube/vitess/go/vt/proto/query"
|
||||
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
|
||||
vtgatepb "github.com/youtube/vitess/go/vt/proto/vtgate"
|
||||
vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
|
||||
|
@ -155,7 +157,7 @@ func (vtg *VTGate) InitializeConnections(ctx context.Context) (err error) {
|
|||
}
|
||||
|
||||
// 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 topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
func (vtg *VTGate) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
startTime := time.Now()
|
||||
statsKey := []string{"Execute", "Any", strings.ToLower(tabletType.String())}
|
||||
defer vtg.timings.Record(statsKey, startTime)
|
||||
|
@ -166,7 +168,7 @@ func (vtg *VTGate) Execute(ctx context.Context, sql string, bindVariables map[st
|
|||
return nil, errTooManyInFlight
|
||||
}
|
||||
|
||||
qr, err := vtg.router.Execute(ctx, sql, bindVariables, tabletType, session, notInTransaction)
|
||||
qr, err := vtg.router.Execute(ctx, sql, bindVariables, keyspace, tabletType, session, notInTransaction)
|
||||
if err == nil {
|
||||
vtg.rowsReturned.Add(statsKey, int64(len(qr.Rows)))
|
||||
return qr, nil
|
||||
|
@ -175,6 +177,7 @@ func (vtg *VTGate) Execute(ctx context.Context, sql string, bindVariables map[st
|
|||
query := map[string]interface{}{
|
||||
"Sql": sql,
|
||||
"BindVariables": bindVariables,
|
||||
"Keyspace": keyspace,
|
||||
"TabletType": strings.ToLower(tabletType.String()),
|
||||
"Session": session,
|
||||
"NotInTransaction": notInTransaction,
|
||||
|
@ -408,7 +411,7 @@ func (vtg *VTGate) ExecuteBatchKeyspaceIds(ctx context.Context, queries []*vtgat
|
|||
}
|
||||
|
||||
// StreamExecute executes a streaming query by routing based on the values in the query.
|
||||
func (vtg *VTGate) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
func (vtg *VTGate) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
startTime := time.Now()
|
||||
statsKey := []string{"StreamExecute", "Any", strings.ToLower(tabletType.String())}
|
||||
defer vtg.timings.Record(statsKey, startTime)
|
||||
|
@ -424,6 +427,7 @@ func (vtg *VTGate) StreamExecute(ctx context.Context, sql string, bindVariables
|
|||
ctx,
|
||||
sql,
|
||||
bindVariables,
|
||||
keyspace,
|
||||
tabletType,
|
||||
func(reply *sqltypes.Result) error {
|
||||
rowCount += int64(len(reply.Rows))
|
||||
|
@ -436,6 +440,7 @@ func (vtg *VTGate) StreamExecute(ctx context.Context, sql string, bindVariables
|
|||
query := map[string]interface{}{
|
||||
"Sql": sql,
|
||||
"BindVariables": bindVariables,
|
||||
"Keyspace": keyspace,
|
||||
"TabletType": strings.ToLower(tabletType.String()),
|
||||
}
|
||||
logError(err, query, vtg.logStreamExecute)
|
||||
|
@ -627,6 +632,132 @@ func (vtg *VTGate) SplitQuery(ctx context.Context, keyspace string, sql string,
|
|||
return vtg.resolver.scatterConn.SplitQueryCustomSharding(ctx, sql, bindVariables, splitColumn, perShardSplitCount, shardNames, keyspace)
|
||||
}
|
||||
|
||||
// SplitQueryV2 implements the SplitQuery RPC. This is the new version that
|
||||
// supports multiple split-columns and multiple splitting algorithms.
|
||||
// See the documentation of SplitQueryRequest in "proto/vtgate.proto" for more
|
||||
// information.
|
||||
// TODO(erez): Remove 'SplitQuery' and rename this method to 'SplitQuery' once the migration
|
||||
// to SplitQuery-V2 is done.
|
||||
func (vtg *VTGate) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
keyspace string,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
|
||||
// TODO(erez): Add validation of SplitQuery parameters.
|
||||
keyspace, srvKeyspace, shardRefs, err := getKeyspaceShards(
|
||||
ctx, vtg.resolver.toposerv, vtg.resolver.cell, keyspace, topodatapb.TabletType_RDONLY)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the caller specified a splitCount (vs. specifying 'numRowsPerQueryPart') scale it by the
|
||||
// number of shards (otherwise it stays 0).
|
||||
perShardSplitCount := int64(math.Ceil(float64(splitCount) / float64(len(shardRefs))))
|
||||
|
||||
// Determine whether to return SplitQueryResponse_KeyRangeParts or SplitQueryResponse_ShardParts.
|
||||
// We return 'KeyRangeParts' for sharded keyspaces that are not custom sharded. If the
|
||||
// keyspace is custom sharded or unsharded we return 'ShardParts'.
|
||||
var querySplitToQueryPartFunc func(
|
||||
querySplit *querytypes.QuerySplit, shard string) (*vtgatepb.SplitQueryResponse_Part, error)
|
||||
// sharding_column_type != KeyspaceIdType_UNSET can happen in one of the following two cases:
|
||||
// 1. We are querying a sharded keyspace;
|
||||
// 2. We are querying an unsharded keyspace which is being sharded.
|
||||
if srvKeyspace.ShardingColumnType != topodatapb.KeyspaceIdType_UNSET {
|
||||
// Index the shard references in 'shardRefs' by shard name.
|
||||
shardRefByName := make(map[string]*topodatapb.ShardReference, len(shardRefs))
|
||||
for _, shardRef := range shardRefs {
|
||||
shardRefByName[shardRef.Name] = shardRef
|
||||
}
|
||||
querySplitToQueryPartFunc = getQuerySplitToKeyRangePartFunc(keyspace, shardRefByName)
|
||||
} else {
|
||||
// Keyspace is either unsharded or custom-sharded.
|
||||
querySplitToQueryPartFunc = getQuerySplitToShardPartFunc(keyspace)
|
||||
}
|
||||
|
||||
// Collect all shard names into a slice.
|
||||
shardNames := make([]string, 0, len(shardRefs))
|
||||
for _, shardRef := range shardRefs {
|
||||
shardNames = append(shardNames, shardRef.Name)
|
||||
}
|
||||
return vtg.resolver.scatterConn.SplitQueryV2(
|
||||
ctx,
|
||||
sql,
|
||||
bindVariables,
|
||||
splitColumns,
|
||||
perShardSplitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm,
|
||||
shardNames,
|
||||
querySplitToQueryPartFunc,
|
||||
keyspace)
|
||||
}
|
||||
|
||||
// getQuerySplitToKeyRangePartFunc returns a function to use with scatterConn.SplitQueryV2
|
||||
// that converts the given QuerySplit to a SplitQueryResponse_Part message whose KeyRangePart field
|
||||
// is set.
|
||||
func getQuerySplitToKeyRangePartFunc(
|
||||
keyspace string,
|
||||
shardReferenceByName map[string]*topodatapb.ShardReference) func(
|
||||
querySplit *querytypes.QuerySplit, shard string) (*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
|
||||
return func(
|
||||
querySplit *querytypes.QuerySplit, shard string) (*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
// TODO(erez): Assert that shardReferenceByName contains an entry for 'shard'.
|
||||
// Keyrange can be nil for the shard (e.g. for single-sharded keyspaces during resharding).
|
||||
// In this case we append an empty keyrange that represents the entire keyspace.
|
||||
keyranges := []*topodatapb.KeyRange{{Start: []byte{}, End: []byte{}}}
|
||||
if shardReferenceByName[shard].KeyRange != nil {
|
||||
keyranges = []*topodatapb.KeyRange{shardReferenceByName[shard].KeyRange}
|
||||
}
|
||||
bindVars, err := querytypes.BindVariablesToProto3(querySplit.BindVariables)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vtgatepb.SplitQueryResponse_Part{
|
||||
Query: &querypb.BoundQuery{
|
||||
Sql: querySplit.Sql,
|
||||
BindVariables: bindVars,
|
||||
},
|
||||
KeyRangePart: &vtgatepb.SplitQueryResponse_KeyRangePart{
|
||||
Keyspace: keyspace,
|
||||
KeyRanges: keyranges,
|
||||
},
|
||||
Size: querySplit.RowCount,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// getQuerySplitToShardPartFunc returns a function to use with scatterConn.SplitQueryV2
|
||||
// that converts the given QuerySplit to a SplitQueryResponse_Part message whose ShardPart field
|
||||
// is set.
|
||||
func getQuerySplitToShardPartFunc(keyspace string) func(
|
||||
querySplit *querytypes.QuerySplit, shard string) (*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
|
||||
return func(
|
||||
querySplit *querytypes.QuerySplit, shard string) (*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
bindVars, err := querytypes.BindVariablesToProto3(querySplit.BindVariables)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &vtgatepb.SplitQueryResponse_Part{
|
||||
Query: &querypb.BoundQuery{
|
||||
Sql: querySplit.Sql,
|
||||
BindVariables: bindVars,
|
||||
},
|
||||
ShardPart: &vtgatepb.SplitQueryResponse_ShardPart{
|
||||
Keyspace: keyspace,
|
||||
Shards: []string{shard},
|
||||
},
|
||||
Size: querySplit.RowCount,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetSrvKeyspace is part of the vtgate service API.
|
||||
func (vtg *VTGate) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) {
|
||||
return vtg.resolver.toposerv.GetSrvKeyspace(ctx, vtg.resolver.cell, keyspace)
|
||||
|
|
|
@ -7,6 +7,7 @@ package vtgate
|
|||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -34,7 +35,7 @@ func init() {
|
|||
"TestUnsharded": {
|
||||
"Sharded": false,
|
||||
"Tables": {
|
||||
"t1": ""
|
||||
"t1": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +51,7 @@ func TestVTGateExecute(t *testing.T) {
|
|||
qr, err := rpcVTGate.Execute(context.Background(),
|
||||
"select id from t1",
|
||||
nil,
|
||||
"",
|
||||
topodatapb.TabletType_MASTER,
|
||||
nil,
|
||||
false)
|
||||
|
@ -67,6 +69,7 @@ func TestVTGateExecute(t *testing.T) {
|
|||
rpcVTGate.Execute(context.Background(),
|
||||
"select id from t1",
|
||||
nil,
|
||||
"",
|
||||
topodatapb.TabletType_MASTER,
|
||||
session,
|
||||
false)
|
||||
|
@ -94,12 +97,43 @@ func TestVTGateExecute(t *testing.T) {
|
|||
rpcVTGate.Execute(context.Background(),
|
||||
"select id from t1",
|
||||
nil,
|
||||
"",
|
||||
topodatapb.TabletType_MASTER,
|
||||
session,
|
||||
false)
|
||||
rpcVTGate.Rollback(context.Background(), session)
|
||||
}
|
||||
|
||||
func TestVTGateExecuteWithKeyspace(t *testing.T) {
|
||||
sandbox := createSandbox(KsTestUnsharded)
|
||||
sbc := &sandboxConn{}
|
||||
sandbox.MapTestConn("0", sbc)
|
||||
qr, err := rpcVTGate.Execute(context.Background(),
|
||||
"select id from none",
|
||||
nil,
|
||||
KsTestUnsharded,
|
||||
topodatapb.TabletType_MASTER,
|
||||
nil,
|
||||
false)
|
||||
if err != nil {
|
||||
t.Errorf("want nil, got %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(singleRowResult, qr) {
|
||||
t.Errorf("want \n%+v, got \n%+v", singleRowResult, qr)
|
||||
}
|
||||
_, err = rpcVTGate.Execute(context.Background(),
|
||||
"select id from none",
|
||||
nil,
|
||||
"aa",
|
||||
topodatapb.TabletType_MASTER,
|
||||
nil,
|
||||
false)
|
||||
want := "keyspace aa not found in vschema"
|
||||
if err == nil || err.Error() != want {
|
||||
t.Errorf("Execute: %v, want %s", err, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVTGateExecuteShards(t *testing.T) {
|
||||
sandbox := createSandbox("TestVTGateExecuteShards")
|
||||
sbc := &sandboxConn{}
|
||||
|
@ -531,6 +565,7 @@ func TestVTGateStreamExecute(t *testing.T) {
|
|||
err := rpcVTGate.StreamExecute(context.Background(),
|
||||
"select id from t1",
|
||||
nil,
|
||||
"",
|
||||
topodatapb.TabletType_MASTER,
|
||||
func(r *sqltypes.Result) error {
|
||||
qrs = append(qrs, r)
|
||||
|
@ -728,6 +763,159 @@ func TestVTGateSplitQuery(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(erez): Rename after migration to SplitQuery V2 is done.
|
||||
func TestVTGateSplitQueryV2Sharded(t *testing.T) {
|
||||
keyspace := "TestVTGateSplitQuery"
|
||||
keyranges, err := key.ParseShardingSpec(DefaultShardSpec)
|
||||
if err != nil {
|
||||
t.Fatalf("got: %v, want: nil", err)
|
||||
}
|
||||
s := createSandbox(keyspace)
|
||||
for _, kr := range keyranges {
|
||||
s.MapTestConn(key.KeyRangeString(kr), &sandboxConn{})
|
||||
}
|
||||
sql := "select col1, col2 from table"
|
||||
bindVars := map[string]interface{}{"bv1": nil}
|
||||
splitColumns := []string{"sc1", "sc2"}
|
||||
algorithm := querypb.SplitQueryRequest_FULL_SCAN
|
||||
type testCaseType struct {
|
||||
splitCount int64
|
||||
numRowsPerQueryPart int64
|
||||
algorithm querypb.SplitQueryRequest_Algorithm
|
||||
}
|
||||
testCases := []testCaseType{
|
||||
{splitCount: 100, numRowsPerQueryPart: 0},
|
||||
{splitCount: 0, numRowsPerQueryPart: 123},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
splits, err := rpcVTGate.SplitQueryV2(
|
||||
context.Background(),
|
||||
keyspace,
|
||||
sql,
|
||||
bindVars,
|
||||
splitColumns,
|
||||
testCase.splitCount,
|
||||
testCase.numRowsPerQueryPart,
|
||||
algorithm)
|
||||
if err != nil {
|
||||
t.Errorf("got %v, want: nil. testCase: %+v", err, testCase)
|
||||
}
|
||||
// Total number of splits should be number of shards as our sandbox returns a single split
|
||||
// for its fake implementation of SplitQuery.
|
||||
if len(keyranges) != len(splits) {
|
||||
t.Errorf("wrong number of splits, got %+v, want %+v. testCase:\n%+v",
|
||||
len(splits), len(keyranges), testCase)
|
||||
}
|
||||
actualSqlsByKeyRange := map[string][]string{}
|
||||
for _, split := range splits {
|
||||
if split.KeyRangePart.Keyspace != keyspace {
|
||||
t.Errorf("wrong keyspace, got \n%+v, want \n%+v. testCase:\n%+v",
|
||||
keyspace, split.KeyRangePart.Keyspace, testCase)
|
||||
}
|
||||
if len(split.KeyRangePart.KeyRanges) != 1 {
|
||||
t.Errorf("wrong number of keyranges, got \n%+v, want \n%+v. testCase:\n%+v",
|
||||
1, len(split.KeyRangePart.KeyRanges), testCase)
|
||||
}
|
||||
kr := key.KeyRangeString(split.KeyRangePart.KeyRanges[0])
|
||||
actualSqlsByKeyRange[kr] = append(actualSqlsByKeyRange[kr], split.Query.Sql)
|
||||
}
|
||||
expectedSqlsByKeyRange := map[string][]string{}
|
||||
for _, kr := range keyranges {
|
||||
perShardSplitCount := int64(math.Ceil(float64(testCase.splitCount) / float64(len(keyranges))))
|
||||
shard := key.KeyRangeString(kr)
|
||||
expectedSqlsByKeyRange[shard] = []string{
|
||||
fmt.Sprintf(
|
||||
"query:%v, splitColumns:%v, splitCount:%v,"+
|
||||
" numRowsPerQueryPart:%v, algorithm:%v, shard:%v",
|
||||
querytypes.BoundQuery{Sql: sql, BindVariables: bindVars},
|
||||
splitColumns,
|
||||
perShardSplitCount,
|
||||
testCase.numRowsPerQueryPart,
|
||||
algorithm,
|
||||
shard,
|
||||
),
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(actualSqlsByKeyRange, expectedSqlsByKeyRange) {
|
||||
t.Errorf(
|
||||
"splits contain the wrong sqls and/or keyranges, "+
|
||||
"got:\n%+v\n, want:\n%+v\n. testCase:\n%+v",
|
||||
actualSqlsByKeyRange, expectedSqlsByKeyRange, testCase)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVTGateSplitQueryV2Unsharded(t *testing.T) {
|
||||
keyspace := KsTestUnsharded
|
||||
s := getSandbox(keyspace)
|
||||
if s == nil {
|
||||
t.Fatalf("Can't find unsharded sandbox.")
|
||||
}
|
||||
s.MapTestConn("0", &sandboxConn{})
|
||||
sql := "select col1, col2 from table"
|
||||
bindVars := map[string]interface{}{"bv1": nil}
|
||||
splitColumns := []string{"sc1", "sc2"}
|
||||
algorithm := querypb.SplitQueryRequest_FULL_SCAN
|
||||
type testCaseType struct {
|
||||
splitCount int64
|
||||
numRowsPerQueryPart int64
|
||||
algorithm querypb.SplitQueryRequest_Algorithm
|
||||
}
|
||||
testCases := []testCaseType{
|
||||
{splitCount: 100, numRowsPerQueryPart: 0},
|
||||
{splitCount: 0, numRowsPerQueryPart: 123},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
splits, err := rpcVTGate.SplitQueryV2(
|
||||
context.Background(),
|
||||
keyspace,
|
||||
sql,
|
||||
bindVars,
|
||||
splitColumns,
|
||||
testCase.splitCount,
|
||||
testCase.numRowsPerQueryPart,
|
||||
algorithm)
|
||||
if err != nil {
|
||||
t.Errorf("got %v, want: nil. testCase: %+v", err, testCase)
|
||||
}
|
||||
// Total number of splits should be number of shards (1) as our sandbox returns a single split
|
||||
// for its fake implementation of SplitQuery.
|
||||
if 1 != len(splits) {
|
||||
t.Errorf("wrong number of splits, got %+v, want %+v. testCase:\n%+v",
|
||||
len(splits), 1, testCase)
|
||||
continue
|
||||
}
|
||||
split := splits[0]
|
||||
if split.KeyRangePart != nil {
|
||||
t.Errorf("KeyRangePart should not be populated. Got:\n%+v\n, testCase:\n%+v\n",
|
||||
keyspace, split.KeyRangePart)
|
||||
}
|
||||
if split.ShardPart.Keyspace != keyspace {
|
||||
t.Errorf("wrong keyspace, got \n%+v, want \n%+v. testCase:\n%+v",
|
||||
keyspace, split.ShardPart.Keyspace, testCase)
|
||||
}
|
||||
if len(split.ShardPart.Shards) != 1 {
|
||||
t.Errorf("wrong number of shards, got \n%+v, want \n%+v. testCase:\n%+v",
|
||||
1, len(split.ShardPart.Shards), testCase)
|
||||
}
|
||||
expectedShard := "0"
|
||||
expectedSQL := fmt.Sprintf(
|
||||
"query:%v, splitColumns:%v, splitCount:%v,"+
|
||||
" numRowsPerQueryPart:%v, algorithm:%v, shard:%v",
|
||||
querytypes.BoundQuery{Sql: sql, BindVariables: bindVars},
|
||||
splitColumns,
|
||||
testCase.splitCount,
|
||||
testCase.numRowsPerQueryPart,
|
||||
algorithm,
|
||||
expectedShard,
|
||||
)
|
||||
if split.Query.Sql != expectedSQL {
|
||||
t.Errorf("got:\n%v\n, want:\n%v\n, testCase:\n%+v",
|
||||
split.Query.Sql, expectedSQL, testCase)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsErrorCausedByVTGate(t *testing.T) {
|
||||
unknownError := fmt.Errorf("unknown error")
|
||||
serverError := &tabletconn.ServerError{
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/youtube/vitess/go/sqltypes"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
querypb "github.com/youtube/vitess/go/vt/proto/query"
|
||||
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
|
||||
vtgatepb "github.com/youtube/vitess/go/vt/proto/vtgate"
|
||||
)
|
||||
|
@ -150,6 +151,25 @@ func (conn *VTGateConn) SplitQuery(ctx context.Context, keyspace string, query s
|
|||
return conn.impl.SplitQuery(ctx, keyspace, query, bindVars, splitColumn, splitCount)
|
||||
}
|
||||
|
||||
// SplitQueryV2 splits a query into smaller queries. It is mostly used by batch job frameworks
|
||||
// such as MapReduce. See the documentation for the vtgate.SplitQueryRequest protocol buffer message
|
||||
// in 'proto/vtgate.proto'.
|
||||
// TODO(erez): Rename to SplitQuery after the migration to SplitQuery V2 is done.
|
||||
func (conn *VTGateConn) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
keyspace string,
|
||||
query string,
|
||||
bindVars map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm,
|
||||
) ([]*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
|
||||
return conn.impl.SplitQueryV2(
|
||||
ctx, keyspace, query, bindVars, splitColumns, splitCount, numRowsPerQueryPart, algorithm)
|
||||
}
|
||||
|
||||
// GetSrvKeyspace returns a topo.SrvKeyspace object.
|
||||
func (conn *VTGateConn) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) {
|
||||
return conn.impl.GetSrvKeyspace(ctx, keyspace)
|
||||
|
@ -334,6 +354,20 @@ type Impl interface {
|
|||
// appending primary key range clauses to the original query.
|
||||
SplitQuery(ctx context.Context, keyspace string, query string, bindVars map[string]interface{}, splitColumn string, splitCount int64) ([]*vtgatepb.SplitQueryResponse_Part, error)
|
||||
|
||||
// SplitQuery splits a query into smaller queries. It is mostly used by batch job frameworks
|
||||
// such as MapReduce. See the documentation for the vtgate.SplitQueryRequest protocol buffer
|
||||
// message in 'proto/vtgate.proto'.
|
||||
// TODO(erez): Rename to SplitQuery after the migration to SplitQuery V2 is done.
|
||||
SplitQueryV2(
|
||||
ctx context.Context,
|
||||
keyspace string,
|
||||
query string,
|
||||
bindVars map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error)
|
||||
|
||||
// GetSrvKeyspace returns a topo.SrvKeyspace.
|
||||
GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error)
|
||||
|
||||
|
|
|
@ -74,13 +74,14 @@ func (f *fakeVTGateService) checkCallerID(ctx context.Context, name string) {
|
|||
type queryExecute struct {
|
||||
SQL string
|
||||
BindVariables map[string]interface{}
|
||||
Keyspace string
|
||||
TabletType topodatapb.TabletType
|
||||
Session *vtgatepb.Session
|
||||
NotInTransaction bool
|
||||
}
|
||||
|
||||
// Execute is part of the VTGateService interface
|
||||
func (f *fakeVTGateService) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
func (f *fakeVTGateService) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
if f.hasError {
|
||||
return nil, errTestVtGateError
|
||||
}
|
||||
|
@ -95,6 +96,7 @@ func (f *fakeVTGateService) Execute(ctx context.Context, sql string, bindVariabl
|
|||
query := &queryExecute{
|
||||
SQL: sql,
|
||||
BindVariables: bindVariables,
|
||||
Keyspace: keyspace,
|
||||
TabletType: tabletType,
|
||||
Session: session,
|
||||
NotInTransaction: notInTransaction,
|
||||
|
@ -370,7 +372,7 @@ func (f *fakeVTGateService) ExecuteBatchKeyspaceIds(ctx context.Context, queries
|
|||
}
|
||||
|
||||
// StreamExecute is part of the VTGateService interface
|
||||
func (f *fakeVTGateService) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
func (f *fakeVTGateService) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
if f.panics {
|
||||
panic(fmt.Errorf("test forced panic"))
|
||||
}
|
||||
|
@ -382,6 +384,7 @@ func (f *fakeVTGateService) StreamExecute(ctx context.Context, sql string, bindV
|
|||
query := &queryExecute{
|
||||
SQL: sql,
|
||||
BindVariables: bindVariables,
|
||||
Keyspace: keyspace,
|
||||
TabletType: tabletType,
|
||||
}
|
||||
if !reflect.DeepEqual(query, execCase.execQuery) {
|
||||
|
@ -630,6 +633,51 @@ func (f *fakeVTGateService) SplitQuery(ctx context.Context, keyspace string, sql
|
|||
return splitQueryResult, nil
|
||||
}
|
||||
|
||||
// querySplitQueryV2 contains all the fields we use to test SplitQuery
|
||||
// TODO(erez): Rename to querySplitQuery after the migration to SplitQuery is done.
|
||||
type querySplitQueryV2 struct {
|
||||
Keyspace string
|
||||
SQL string
|
||||
BindVariables map[string]interface{}
|
||||
SplitColumns []string
|
||||
SplitCount int64
|
||||
NumRowsPerQueryPart int64
|
||||
Algorithm querypb.SplitQueryRequest_Algorithm
|
||||
}
|
||||
|
||||
// SplitQueryV2 is part of the VTGateService interface
|
||||
// TODO(erez): Rename to SplitQuery after the migration to SplitQuery is done.
|
||||
func (f *fakeVTGateService) SplitQueryV2(
|
||||
ctx context.Context,
|
||||
keyspace string,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
if f.hasError {
|
||||
return nil, errTestVtGateError
|
||||
}
|
||||
if f.panics {
|
||||
panic(fmt.Errorf("test forced panic"))
|
||||
}
|
||||
f.checkCallerID(ctx, "SplitQuery")
|
||||
query := &querySplitQueryV2{
|
||||
Keyspace: keyspace,
|
||||
SQL: sql,
|
||||
BindVariables: bindVariables,
|
||||
SplitColumns: splitColumns,
|
||||
SplitCount: splitCount,
|
||||
NumRowsPerQueryPart: numRowsPerQueryPart,
|
||||
Algorithm: algorithm,
|
||||
}
|
||||
if !reflect.DeepEqual(query, splitQueryV2Request) {
|
||||
f.t.Errorf("SplitQuery has wrong input: got %#v wanted %#v", query, splitQueryRequest)
|
||||
}
|
||||
return splitQueryResult, nil
|
||||
}
|
||||
|
||||
// GetSrvKeyspace is part of the VTGateService interface
|
||||
func (f *fakeVTGateService) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error) {
|
||||
if f.hasError {
|
||||
|
@ -705,6 +753,7 @@ func TestSuite(t *testing.T, impl vtgateconn.Impl, fakeServer vtgateservice.VTGa
|
|||
testTx2Pass(t, conn)
|
||||
testTx2Fail(t, conn)
|
||||
testSplitQuery(t, conn)
|
||||
testSplitQueryV2(t, conn)
|
||||
testGetSrvKeyspace(t, conn)
|
||||
|
||||
// force a panic at every call, then test that works
|
||||
|
@ -732,6 +781,7 @@ func TestSuite(t *testing.T, impl vtgateconn.Impl, fakeServer vtgateservice.VTGa
|
|||
testStreamExecuteKeyRangesPanic(t, conn)
|
||||
testStreamExecuteKeyspaceIdsPanic(t, conn)
|
||||
testSplitQueryPanic(t, conn)
|
||||
testSplitQueryV2Panic(t, conn)
|
||||
testGetSrvKeyspacePanic(t, conn)
|
||||
fs.panics = false
|
||||
}
|
||||
|
@ -770,6 +820,7 @@ func TestErrorSuite(t *testing.T, fakeServer vtgateservice.VTGateService) {
|
|||
testStreamExecuteKeyRangesError(t, conn, fs)
|
||||
testStreamExecuteKeyspaceIdsError(t, conn, fs)
|
||||
testSplitQueryError(t, conn)
|
||||
testSplitQueryV2Error(t, conn)
|
||||
testGetSrvKeyspaceError(t, conn)
|
||||
fs.hasError = false
|
||||
}
|
||||
|
@ -1879,15 +1930,72 @@ func testSplitQuery(t *testing.T, conn *vtgateconn.VTGateConn) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(erez): Rename to testSplitQuery after migration to SplitQuery V2 is done.
|
||||
func testSplitQueryV2(t *testing.T, conn *vtgateconn.VTGateConn) {
|
||||
ctx := newContext()
|
||||
qsl, err := conn.SplitQueryV2(ctx,
|
||||
splitQueryV2Request.Keyspace,
|
||||
splitQueryV2Request.SQL,
|
||||
splitQueryV2Request.BindVariables,
|
||||
splitQueryV2Request.SplitColumns,
|
||||
splitQueryV2Request.SplitCount,
|
||||
splitQueryV2Request.NumRowsPerQueryPart,
|
||||
splitQueryV2Request.Algorithm,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("SplitQuery failed: %v", err)
|
||||
}
|
||||
if len(qsl) == 1 && len(qsl[0].Query.BindVariables) == 1 {
|
||||
bv := qsl[0].Query.BindVariables["bind1"]
|
||||
if len(bv.Values) == 0 {
|
||||
bv.Values = nil
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(qsl, splitQueryResult) {
|
||||
t.Errorf("SplitQuery returned wrong result: got %#v wanted %#v", qsl, splitQueryResult)
|
||||
}
|
||||
}
|
||||
|
||||
func testSplitQueryError(t *testing.T, conn *vtgateconn.VTGateConn) {
|
||||
ctx := newContext()
|
||||
_, err := conn.SplitQuery(ctx, splitQueryRequest.Keyspace, splitQueryRequest.SQL, splitQueryRequest.BindVariables, splitQueryRequest.SplitColumn, splitQueryRequest.SplitCount)
|
||||
verifyError(t, err, "SplitQuery")
|
||||
}
|
||||
|
||||
// TODO(erez): Rename to testSplitQueryError after migration to SplitQuery V2 is done.
|
||||
func testSplitQueryV2Error(t *testing.T, conn *vtgateconn.VTGateConn) {
|
||||
ctx := newContext()
|
||||
_, err := conn.SplitQueryV2(ctx,
|
||||
splitQueryV2Request.Keyspace,
|
||||
splitQueryV2Request.SQL,
|
||||
splitQueryV2Request.BindVariables,
|
||||
splitQueryV2Request.SplitColumns,
|
||||
splitQueryV2Request.SplitCount,
|
||||
splitQueryV2Request.NumRowsPerQueryPart,
|
||||
splitQueryV2Request.Algorithm,
|
||||
)
|
||||
verifyError(t, err, "SplitQuery")
|
||||
}
|
||||
|
||||
func testSplitQueryPanic(t *testing.T, conn *vtgateconn.VTGateConn) {
|
||||
ctx := newContext()
|
||||
_, err := conn.SplitQuery(ctx, splitQueryRequest.Keyspace, splitQueryRequest.SQL, splitQueryRequest.BindVariables, splitQueryRequest.SplitColumn, splitQueryRequest.SplitCount)
|
||||
|
||||
expectPanic(t, err)
|
||||
}
|
||||
|
||||
// TODO(erez): Rename to testSplitQueryPanic after migration to SplitQuery V2 is done.
|
||||
func testSplitQueryV2Panic(t *testing.T, conn *vtgateconn.VTGateConn) {
|
||||
ctx := newContext()
|
||||
_, err := conn.SplitQueryV2(ctx,
|
||||
splitQueryV2Request.Keyspace,
|
||||
splitQueryV2Request.SQL,
|
||||
splitQueryV2Request.BindVariables,
|
||||
splitQueryV2Request.SplitColumns,
|
||||
splitQueryV2Request.SplitCount,
|
||||
splitQueryV2Request.NumRowsPerQueryPart,
|
||||
splitQueryV2Request.Algorithm,
|
||||
)
|
||||
expectPanic(t, err)
|
||||
}
|
||||
|
||||
|
@ -2315,6 +2423,18 @@ var splitQueryRequest = &querySplitQuery{
|
|||
SplitCount: 13,
|
||||
}
|
||||
|
||||
var splitQueryV2Request = &querySplitQueryV2{
|
||||
Keyspace: "ks2",
|
||||
SQL: "in for SplitQueryV2",
|
||||
BindVariables: map[string]interface{}{
|
||||
"bind2": int64(43),
|
||||
},
|
||||
SplitColumns: []string{"split_column1", "split_column2"},
|
||||
SplitCount: 145,
|
||||
NumRowsPerQueryPart: 4000,
|
||||
Algorithm: querypb.SplitQueryRequest_FULL_SCAN,
|
||||
}
|
||||
|
||||
var splitQueryResult = []*vtgatepb.SplitQueryResponse_Part{
|
||||
{
|
||||
Query: &querypb.BoundQuery{
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/youtube/vitess/go/sqltypes"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
querypb "github.com/youtube/vitess/go/vt/proto/query"
|
||||
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
|
||||
vtgatepb "github.com/youtube/vitess/go/vt/proto/vtgate"
|
||||
)
|
||||
|
@ -19,7 +20,7 @@ import (
|
|||
type VTGateService interface {
|
||||
// Regular query execution.
|
||||
// All these methods can change the provided session.
|
||||
Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error)
|
||||
Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error)
|
||||
ExecuteShards(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, shards []string, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error)
|
||||
ExecuteKeyspaceIds(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, keyspaceIds [][]byte, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error)
|
||||
ExecuteKeyRanges(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, keyRanges []*topodatapb.KeyRange, tabletType topodatapb.TabletType, session *vtgatepb.Session, notInTransaction bool) (*sqltypes.Result, error)
|
||||
|
@ -28,7 +29,7 @@ type VTGateService interface {
|
|||
ExecuteBatchKeyspaceIds(ctx context.Context, queries []*vtgatepb.BoundKeyspaceIdQuery, tabletType topodatapb.TabletType, asTransaction bool, session *vtgatepb.Session) ([]sqltypes.Result, error)
|
||||
|
||||
// Streaming queries
|
||||
StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error
|
||||
StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error
|
||||
StreamExecuteShards(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, shards []string, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error
|
||||
StreamExecuteKeyspaceIds(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, keyspaceIds [][]byte, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error
|
||||
StreamExecuteKeyRanges(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, keyRanges []*topodatapb.KeyRange, tabletType topodatapb.TabletType, sendReply func(*sqltypes.Result) error) error
|
||||
|
@ -41,6 +42,18 @@ type VTGateService interface {
|
|||
// Map Reduce support
|
||||
SplitQuery(ctx context.Context, keyspace string, sql string, bindVariables map[string]interface{}, splitColumn string, splitCount int64) ([]*vtgatepb.SplitQueryResponse_Part, error)
|
||||
|
||||
// MapReduce support
|
||||
// TODO(erez): Rename to SplitQuery after migration to SplitQuery V2.
|
||||
SplitQueryV2(
|
||||
ctx context.Context,
|
||||
keyspace string,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error)
|
||||
|
||||
// Topology support
|
||||
GetSrvKeyspace(ctx context.Context, keyspace string) (*topodatapb.SrvKeyspace, error)
|
||||
|
||||
|
@ -52,3 +65,56 @@ type VTGateService interface {
|
|||
// RPC implementation method, before calling any of the previous methods
|
||||
HandlePanic(err *error)
|
||||
}
|
||||
|
||||
// CallCorrectSplitQuery calls the correct SplitQuery.
|
||||
// This trivial logic is encapsulated in a function here so it can be easily tested.
|
||||
// TODO(erez): Remove once the migration to SplitQueryV2 is done.
|
||||
func CallCorrectSplitQuery(
|
||||
vtgateService VTGateService,
|
||||
useSplitQueryV2 bool,
|
||||
ctx context.Context,
|
||||
keyspace string,
|
||||
sql string,
|
||||
bindVariables map[string]interface{},
|
||||
splitColumns []string,
|
||||
splitCount int64,
|
||||
numRowsPerQueryPart int64,
|
||||
algorithm querypb.SplitQueryRequest_Algorithm) ([]*vtgatepb.SplitQueryResponse_Part, error) {
|
||||
|
||||
if useSplitQueryV2 {
|
||||
return vtgateService.SplitQueryV2(
|
||||
ctx,
|
||||
keyspace,
|
||||
sql,
|
||||
bindVariables,
|
||||
splitColumns,
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm)
|
||||
}
|
||||
return vtgateService.SplitQuery(
|
||||
ctx,
|
||||
keyspace,
|
||||
sql,
|
||||
bindVariables,
|
||||
splitColumnsToSplitColumn(splitColumns),
|
||||
splitCount)
|
||||
}
|
||||
|
||||
// SplitColumnsToSplitColumn returns the first SplitColumn in the given slice or an empty
|
||||
// string if the slice is empty.
|
||||
//
|
||||
// This method is used to get the traditional behavior when accessing the SplitColumn field in an
|
||||
// older SplitQuery-V1 vtgatepb.SplitQueryRequest represented in the newer SplitQuery-V2
|
||||
// vtgatepb.SplitQueryRequest message. In the new V2 message the SplitColumn field has been
|
||||
// converted into a repeated string field.
|
||||
// TODO(erez): Remove this function when migration to SplitQueryV2 is done.
|
||||
func splitColumnsToSplitColumn(splitColumns []string) string {
|
||||
if len(splitColumns) == 0 {
|
||||
return ""
|
||||
}
|
||||
return splitColumns[0]
|
||||
}
|
||||
|
||||
// Command to generate a mock for this interface with mockgen.
|
||||
//go:generate mockgen -source $GOFILE -destination vtgateservice_testing/mock_vtgateservice.go -package vtgateservice_testing
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
package vtgateservice
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
querypb "github.com/youtube/vitess/go/vt/proto/query"
|
||||
"github.com/youtube/vitess/go/vt/vtgate/vtgateservice/vtgateservice_testing"
|
||||
)
|
||||
|
||||
var (
|
||||
keyspace = "It's a keyspace!"
|
||||
sql = "It's an SQL statement!"
|
||||
bindVariables = map[string]interface{}{}
|
||||
splitCount int64 = 123
|
||||
numRowsPerQueryPart int64 = 456
|
||||
algorithm = querypb.SplitQueryRequest_EQUAL_SPLITS
|
||||
)
|
||||
|
||||
func TestCallCorrectSplitQueryCallV1NoSplitColumn(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
mockVTGateService := vtgateservice_testing.NewMockVTGateService(mockCtrl)
|
||||
|
||||
mockVTGateService.EXPECT().SplitQuery(
|
||||
context.Background(),
|
||||
keyspace,
|
||||
sql,
|
||||
bindVariables,
|
||||
"",
|
||||
splitCount)
|
||||
CallCorrectSplitQuery(
|
||||
mockVTGateService,
|
||||
false, /* useSplitQueryV2 */
|
||||
context.Background(),
|
||||
keyspace,
|
||||
sql,
|
||||
bindVariables,
|
||||
[]string{}, /* SplitColumns */
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm)
|
||||
}
|
||||
|
||||
func TestCallCorrectSplitQueryCallV1SplitColumnEmpty(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
mockVTGateService := vtgateservice_testing.NewMockVTGateService(mockCtrl)
|
||||
|
||||
mockVTGateService.EXPECT().SplitQuery(
|
||||
context.Background(),
|
||||
keyspace,
|
||||
sql,
|
||||
bindVariables,
|
||||
"",
|
||||
splitCount)
|
||||
CallCorrectSplitQuery(
|
||||
mockVTGateService,
|
||||
false, /* useSplitQueryV2 */
|
||||
context.Background(),
|
||||
keyspace,
|
||||
sql,
|
||||
bindVariables,
|
||||
[]string{""}, /* SplitColumns */
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm)
|
||||
}
|
||||
|
||||
func TestCallCorrectSplitQueryCallV1WithSplitColumn(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
mockVTGateService := vtgateservice_testing.NewMockVTGateService(mockCtrl)
|
||||
|
||||
mockVTGateService.EXPECT().SplitQuery(
|
||||
context.Background(),
|
||||
keyspace,
|
||||
sql,
|
||||
bindVariables,
|
||||
"First Split Column",
|
||||
splitCount)
|
||||
CallCorrectSplitQuery(
|
||||
mockVTGateService,
|
||||
false, /* useSplitQueryV2 */
|
||||
context.Background(),
|
||||
keyspace,
|
||||
sql,
|
||||
bindVariables,
|
||||
[]string{"First Split Column"}, /* SplitColumns */
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm)
|
||||
}
|
||||
|
||||
func TestCallCorrectSplitQueryCallV2(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
mockVTGateService := vtgateservice_testing.NewMockVTGateService(mockCtrl)
|
||||
splitColumns := []string{"col1", "col2"}
|
||||
mockVTGateService.EXPECT().SplitQueryV2(
|
||||
context.Background(),
|
||||
keyspace,
|
||||
sql,
|
||||
bindVariables,
|
||||
splitColumns,
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm)
|
||||
CallCorrectSplitQuery(
|
||||
mockVTGateService,
|
||||
true, /* useSplitQueryV2 */
|
||||
context.Background(),
|
||||
keyspace,
|
||||
sql,
|
||||
bindVariables,
|
||||
splitColumns,
|
||||
splitCount,
|
||||
numRowsPerQueryPart,
|
||||
algorithm)
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
// Automatically generated by MockGen. DO NOT EDIT!
|
||||
// Source: interface.go
|
||||
|
||||
package vtgateservice_testing
|
||||
|
||||
import (
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
sqltypes "github.com/youtube/vitess/go/sqltypes"
|
||||
query "github.com/youtube/vitess/go/vt/proto/query"
|
||||
topodata "github.com/youtube/vitess/go/vt/proto/topodata"
|
||||
vtgate "github.com/youtube/vitess/go/vt/proto/vtgate"
|
||||
context "golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Mock of VTGateService interface
|
||||
type MockVTGateService struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *_MockVTGateServiceRecorder
|
||||
}
|
||||
|
||||
// Recorder for MockVTGateService (not exported)
|
||||
type _MockVTGateServiceRecorder struct {
|
||||
mock *MockVTGateService
|
||||
}
|
||||
|
||||
func NewMockVTGateService(ctrl *gomock.Controller) *MockVTGateService {
|
||||
mock := &MockVTGateService{ctrl: ctrl}
|
||||
mock.recorder = &_MockVTGateServiceRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) EXPECT() *_MockVTGateServiceRecorder {
|
||||
return _m.recorder
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) Execute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodata.TabletType, session *vtgate.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
ret := _m.ctrl.Call(_m, "Execute", ctx, sql, bindVariables, keyspace, tabletType, session, notInTransaction)
|
||||
ret0, _ := ret[0].(*sqltypes.Result)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) Execute(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Execute", arg0, arg1, arg2, arg3, arg4, arg5, arg6)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) ExecuteShards(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, shards []string, tabletType topodata.TabletType, session *vtgate.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
ret := _m.ctrl.Call(_m, "ExecuteShards", ctx, sql, bindVariables, keyspace, shards, tabletType, session, notInTransaction)
|
||||
ret0, _ := ret[0].(*sqltypes.Result)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) ExecuteShards(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "ExecuteShards", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) ExecuteKeyspaceIds(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, keyspaceIds [][]byte, tabletType topodata.TabletType, session *vtgate.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
ret := _m.ctrl.Call(_m, "ExecuteKeyspaceIds", ctx, sql, bindVariables, keyspace, keyspaceIds, tabletType, session, notInTransaction)
|
||||
ret0, _ := ret[0].(*sqltypes.Result)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) ExecuteKeyspaceIds(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "ExecuteKeyspaceIds", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) ExecuteKeyRanges(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, keyRanges []*topodata.KeyRange, tabletType topodata.TabletType, session *vtgate.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
ret := _m.ctrl.Call(_m, "ExecuteKeyRanges", ctx, sql, bindVariables, keyspace, keyRanges, tabletType, session, notInTransaction)
|
||||
ret0, _ := ret[0].(*sqltypes.Result)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) ExecuteKeyRanges(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "ExecuteKeyRanges", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) ExecuteEntityIds(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, entityColumnName string, entityKeyspaceIDs []*vtgate.ExecuteEntityIdsRequest_EntityId, tabletType topodata.TabletType, session *vtgate.Session, notInTransaction bool) (*sqltypes.Result, error) {
|
||||
ret := _m.ctrl.Call(_m, "ExecuteEntityIds", ctx, sql, bindVariables, keyspace, entityColumnName, entityKeyspaceIDs, tabletType, session, notInTransaction)
|
||||
ret0, _ := ret[0].(*sqltypes.Result)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) ExecuteEntityIds(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "ExecuteEntityIds", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) ExecuteBatchShards(ctx context.Context, queries []*vtgate.BoundShardQuery, tabletType topodata.TabletType, asTransaction bool, session *vtgate.Session) ([]sqltypes.Result, error) {
|
||||
ret := _m.ctrl.Call(_m, "ExecuteBatchShards", ctx, queries, tabletType, asTransaction, session)
|
||||
ret0, _ := ret[0].([]sqltypes.Result)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) ExecuteBatchShards(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "ExecuteBatchShards", arg0, arg1, arg2, arg3, arg4)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) ExecuteBatchKeyspaceIds(ctx context.Context, queries []*vtgate.BoundKeyspaceIdQuery, tabletType topodata.TabletType, asTransaction bool, session *vtgate.Session) ([]sqltypes.Result, error) {
|
||||
ret := _m.ctrl.Call(_m, "ExecuteBatchKeyspaceIds", ctx, queries, tabletType, asTransaction, session)
|
||||
ret0, _ := ret[0].([]sqltypes.Result)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) ExecuteBatchKeyspaceIds(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "ExecuteBatchKeyspaceIds", arg0, arg1, arg2, arg3, arg4)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) StreamExecute(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, tabletType topodata.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
ret := _m.ctrl.Call(_m, "StreamExecute", ctx, sql, bindVariables, keyspace, tabletType, sendReply)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) StreamExecute(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "StreamExecute", arg0, arg1, arg2, arg3, arg4, arg5)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) StreamExecuteShards(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, shards []string, tabletType topodata.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
ret := _m.ctrl.Call(_m, "StreamExecuteShards", ctx, sql, bindVariables, keyspace, shards, tabletType, sendReply)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) StreamExecuteShards(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "StreamExecuteShards", arg0, arg1, arg2, arg3, arg4, arg5, arg6)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) StreamExecuteKeyspaceIds(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, keyspaceIds [][]byte, tabletType topodata.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
ret := _m.ctrl.Call(_m, "StreamExecuteKeyspaceIds", ctx, sql, bindVariables, keyspace, keyspaceIds, tabletType, sendReply)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) StreamExecuteKeyspaceIds(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "StreamExecuteKeyspaceIds", arg0, arg1, arg2, arg3, arg4, arg5, arg6)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) StreamExecuteKeyRanges(ctx context.Context, sql string, bindVariables map[string]interface{}, keyspace string, keyRanges []*topodata.KeyRange, tabletType topodata.TabletType, sendReply func(*sqltypes.Result) error) error {
|
||||
ret := _m.ctrl.Call(_m, "StreamExecuteKeyRanges", ctx, sql, bindVariables, keyspace, keyRanges, tabletType, sendReply)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) StreamExecuteKeyRanges(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "StreamExecuteKeyRanges", arg0, arg1, arg2, arg3, arg4, arg5, arg6)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) Begin(ctx context.Context) (*vtgate.Session, error) {
|
||||
ret := _m.ctrl.Call(_m, "Begin", ctx)
|
||||
ret0, _ := ret[0].(*vtgate.Session)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) Begin(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Begin", arg0)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) Commit(ctx context.Context, session *vtgate.Session) error {
|
||||
ret := _m.ctrl.Call(_m, "Commit", ctx, session)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) Commit(arg0, arg1 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Commit", arg0, arg1)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) Rollback(ctx context.Context, session *vtgate.Session) error {
|
||||
ret := _m.ctrl.Call(_m, "Rollback", ctx, session)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) Rollback(arg0, arg1 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "Rollback", arg0, arg1)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) SplitQuery(ctx context.Context, keyspace string, sql string, bindVariables map[string]interface{}, splitColumn string, splitCount int64) ([]*vtgate.SplitQueryResponse_Part, error) {
|
||||
ret := _m.ctrl.Call(_m, "SplitQuery", ctx, keyspace, sql, bindVariables, splitColumn, splitCount)
|
||||
ret0, _ := ret[0].([]*vtgate.SplitQueryResponse_Part)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) SplitQuery(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "SplitQuery", arg0, arg1, arg2, arg3, arg4, arg5)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) SplitQueryV2(ctx context.Context, keyspace string, sql string, bindVariables map[string]interface{}, splitColumns []string, splitCount int64, numRowsPerQueryPart int64, algorithm query.SplitQueryRequest_Algorithm) ([]*vtgate.SplitQueryResponse_Part, error) {
|
||||
ret := _m.ctrl.Call(_m, "SplitQueryV2", ctx, keyspace, sql, bindVariables, splitColumns, splitCount, numRowsPerQueryPart, algorithm)
|
||||
ret0, _ := ret[0].([]*vtgate.SplitQueryResponse_Part)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) SplitQueryV2(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "SplitQueryV2", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) GetSrvKeyspace(ctx context.Context, keyspace string) (*topodata.SrvKeyspace, error) {
|
||||
ret := _m.ctrl.Call(_m, "GetSrvKeyspace", ctx, keyspace)
|
||||
ret0, _ := ret[0].(*topodata.SrvKeyspace)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) GetSrvKeyspace(arg0, arg1 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "GetSrvKeyspace", arg0, arg1)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) GetSrvShard(ctx context.Context, keyspace string, shard string) (*topodata.SrvShard, error) {
|
||||
ret := _m.ctrl.Call(_m, "GetSrvShard", ctx, keyspace, shard)
|
||||
ret0, _ := ret[0].(*topodata.SrvShard)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) GetSrvShard(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "GetSrvShard", arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
func (_m *MockVTGateService) HandlePanic(err *error) {
|
||||
_m.ctrl.Call(_m, "HandlePanic", err)
|
||||
}
|
||||
|
||||
func (_mr *_MockVTGateServiceRecorder) HandlePanic(arg0 interface{}) *gomock.Call {
|
||||
return _mr.mock.ctrl.RecordCall(_mr.mock, "HandlePanic", arg0)
|
||||
}
|
|
@ -371,6 +371,7 @@ public final class VTGateConn implements Closeable {
|
|||
}));
|
||||
}
|
||||
|
||||
// TODO(erez): Migrate to SplitQueryV2 after it's stable.
|
||||
public SQLFuture<List<SplitQueryResponse.Part>> splitQuery(
|
||||
Context ctx,
|
||||
String keyspace,
|
||||
|
@ -383,8 +384,9 @@ public final class VTGateConn implements Closeable {
|
|||
SplitQueryRequest.newBuilder()
|
||||
.setKeyspace(checkNotNull(keyspace))
|
||||
.setQuery(Proto.bindQuery(checkNotNull(query), bindVars))
|
||||
.setSplitColumn(checkNotNull(splitColumn))
|
||||
.setSplitCount(splitCount);
|
||||
.addSplitColumn(checkNotNull(splitColumn))
|
||||
.setSplitCount(splitCount)
|
||||
.setUseSplitQueryV2(false);
|
||||
if (ctx.getCallerId() != null) {
|
||||
requestBuilder.setCallerId(ctx.getCallerId());
|
||||
}
|
||||
|
|
|
@ -18,15 +18,24 @@ namespace Vitess\Proto\Query {
|
|||
/** @var \Vitess\Proto\Query\BoundQuery */
|
||||
public $query = null;
|
||||
|
||||
/** @var string */
|
||||
public $split_column = null;
|
||||
/** @var string[] */
|
||||
public $split_column = array();
|
||||
|
||||
/** @var int */
|
||||
public $split_count = null;
|
||||
|
||||
/** @var int */
|
||||
public $num_rows_per_query_part = null;
|
||||
|
||||
/** @var int */
|
||||
public $session_id = null;
|
||||
|
||||
/** @var int - \Vitess\Proto\Query\SplitQueryRequest\Algorithm */
|
||||
public $algorithm = null;
|
||||
|
||||
/** @var boolean */
|
||||
public $use_split_query_v2 = null;
|
||||
|
||||
|
||||
/** @var \Closure[] */
|
||||
protected static $__extensions = array();
|
||||
|
@ -71,12 +80,12 @@ namespace Vitess\Proto\Query {
|
|||
$f->reference = '\Vitess\Proto\Query\BoundQuery';
|
||||
$descriptor->addField($f);
|
||||
|
||||
// OPTIONAL STRING split_column = 5
|
||||
// REPEATED STRING split_column = 5
|
||||
$f = new \DrSlump\Protobuf\Field();
|
||||
$f->number = 5;
|
||||
$f->name = "split_column";
|
||||
$f->type = \DrSlump\Protobuf::TYPE_STRING;
|
||||
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
|
||||
$f->rule = \DrSlump\Protobuf::RULE_REPEATED;
|
||||
$descriptor->addField($f);
|
||||
|
||||
// OPTIONAL INT64 split_count = 6
|
||||
|
@ -87,6 +96,14 @@ namespace Vitess\Proto\Query {
|
|||
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
|
||||
$descriptor->addField($f);
|
||||
|
||||
// OPTIONAL INT64 num_rows_per_query_part = 8
|
||||
$f = new \DrSlump\Protobuf\Field();
|
||||
$f->number = 8;
|
||||
$f->name = "num_rows_per_query_part";
|
||||
$f->type = \DrSlump\Protobuf::TYPE_INT64;
|
||||
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
|
||||
$descriptor->addField($f);
|
||||
|
||||
// OPTIONAL INT64 session_id = 7
|
||||
$f = new \DrSlump\Protobuf\Field();
|
||||
$f->number = 7;
|
||||
|
@ -95,6 +112,23 @@ namespace Vitess\Proto\Query {
|
|||
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
|
||||
$descriptor->addField($f);
|
||||
|
||||
// OPTIONAL ENUM algorithm = 9
|
||||
$f = new \DrSlump\Protobuf\Field();
|
||||
$f->number = 9;
|
||||
$f->name = "algorithm";
|
||||
$f->type = \DrSlump\Protobuf::TYPE_ENUM;
|
||||
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
|
||||
$f->reference = '\Vitess\Proto\Query\SplitQueryRequest\Algorithm';
|
||||
$descriptor->addField($f);
|
||||
|
||||
// OPTIONAL BOOL use_split_query_v2 = 10
|
||||
$f = new \DrSlump\Protobuf\Field();
|
||||
$f->number = 10;
|
||||
$f->name = "use_split_query_v2";
|
||||
$f->type = \DrSlump\Protobuf::TYPE_BOOL;
|
||||
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
|
||||
$descriptor->addField($f);
|
||||
|
||||
foreach (self::$__extensions as $cb) {
|
||||
$descriptor->addField($cb(), true);
|
||||
}
|
||||
|
@ -271,10 +305,11 @@ namespace Vitess\Proto\Query {
|
|||
/**
|
||||
* Get <split_column> value
|
||||
*
|
||||
* @param int $idx
|
||||
* @return string
|
||||
*/
|
||||
public function getSplitColumn(){
|
||||
return $this->_get(5);
|
||||
public function getSplitColumn($idx = NULL){
|
||||
return $this->_get(5, $idx);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -283,8 +318,27 @@ namespace Vitess\Proto\Query {
|
|||
* @param string $value
|
||||
* @return \Vitess\Proto\Query\SplitQueryRequest
|
||||
*/
|
||||
public function setSplitColumn( $value){
|
||||
return $this->_set(5, $value);
|
||||
public function setSplitColumn( $value, $idx = NULL){
|
||||
return $this->_set(5, $value, $idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all elements of <split_column>
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getSplitColumnList(){
|
||||
return $this->_get(5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new element to <split_column>
|
||||
*
|
||||
* @param string $value
|
||||
* @return \Vitess\Proto\Query\SplitQueryRequest
|
||||
*/
|
||||
public function addSplitColumn( $value){
|
||||
return $this->_add(5, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -324,6 +378,43 @@ namespace Vitess\Proto\Query {
|
|||
return $this->_set(6, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if <num_rows_per_query_part> has a value
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasNumRowsPerQueryPart(){
|
||||
return $this->_has(8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear <num_rows_per_query_part> value
|
||||
*
|
||||
* @return \Vitess\Proto\Query\SplitQueryRequest
|
||||
*/
|
||||
public function clearNumRowsPerQueryPart(){
|
||||
return $this->_clear(8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get <num_rows_per_query_part> value
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNumRowsPerQueryPart(){
|
||||
return $this->_get(8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set <num_rows_per_query_part> value
|
||||
*
|
||||
* @param int $value
|
||||
* @return \Vitess\Proto\Query\SplitQueryRequest
|
||||
*/
|
||||
public function setNumRowsPerQueryPart( $value){
|
||||
return $this->_set(8, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if <session_id> has a value
|
||||
*
|
||||
|
@ -360,6 +451,80 @@ namespace Vitess\Proto\Query {
|
|||
public function setSessionId( $value){
|
||||
return $this->_set(7, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if <algorithm> has a value
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasAlgorithm(){
|
||||
return $this->_has(9);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear <algorithm> value
|
||||
*
|
||||
* @return \Vitess\Proto\Query\SplitQueryRequest
|
||||
*/
|
||||
public function clearAlgorithm(){
|
||||
return $this->_clear(9);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get <algorithm> value
|
||||
*
|
||||
* @return int - \Vitess\Proto\Query\SplitQueryRequest\Algorithm
|
||||
*/
|
||||
public function getAlgorithm(){
|
||||
return $this->_get(9);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set <algorithm> value
|
||||
*
|
||||
* @param int - \Vitess\Proto\Query\SplitQueryRequest\Algorithm $value
|
||||
* @return \Vitess\Proto\Query\SplitQueryRequest
|
||||
*/
|
||||
public function setAlgorithm( $value){
|
||||
return $this->_set(9, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if <use_split_query_v2> has a value
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasUseSplitQueryV2(){
|
||||
return $this->_has(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear <use_split_query_v2> value
|
||||
*
|
||||
* @return \Vitess\Proto\Query\SplitQueryRequest
|
||||
*/
|
||||
public function clearUseSplitQueryV2(){
|
||||
return $this->_clear(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get <use_split_query_v2> value
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getUseSplitQueryV2(){
|
||||
return $this->_get(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set <use_split_query_v2> value
|
||||
*
|
||||
* @param boolean $value
|
||||
* @return \Vitess\Proto\Query\SplitQueryRequest
|
||||
*/
|
||||
public function setUseSplitQueryV2( $value){
|
||||
return $this->_set(10, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
// DO NOT EDIT! Generated by Protobuf-PHP protoc plugin 1.0
|
||||
// Source: query.proto
|
||||
|
||||
namespace Vitess\Proto\Query\SplitQueryRequest {
|
||||
|
||||
class Algorithm extends \DrSlump\Protobuf\Enum {
|
||||
const EQUAL_SPLITS = 0;
|
||||
const FULL_SCAN = 1;
|
||||
}
|
||||
}
|
|
@ -15,12 +15,21 @@ namespace Vitess\Proto\Vtgate {
|
|||
/** @var \Vitess\Proto\Query\BoundQuery */
|
||||
public $query = null;
|
||||
|
||||
/** @var string */
|
||||
public $split_column = null;
|
||||
/** @var string[] */
|
||||
public $split_column = array();
|
||||
|
||||
/** @var int */
|
||||
public $split_count = null;
|
||||
|
||||
/** @var int */
|
||||
public $num_rows_per_query_part = null;
|
||||
|
||||
/** @var int - \Vitess\Proto\Query\SplitQueryRequest\Algorithm */
|
||||
public $algorithm = null;
|
||||
|
||||
/** @var boolean */
|
||||
public $use_split_query_v2 = null;
|
||||
|
||||
|
||||
/** @var \Closure[] */
|
||||
protected static $__extensions = array();
|
||||
|
@ -55,12 +64,12 @@ namespace Vitess\Proto\Vtgate {
|
|||
$f->reference = '\Vitess\Proto\Query\BoundQuery';
|
||||
$descriptor->addField($f);
|
||||
|
||||
// OPTIONAL STRING split_column = 4
|
||||
// REPEATED STRING split_column = 4
|
||||
$f = new \DrSlump\Protobuf\Field();
|
||||
$f->number = 4;
|
||||
$f->name = "split_column";
|
||||
$f->type = \DrSlump\Protobuf::TYPE_STRING;
|
||||
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
|
||||
$f->rule = \DrSlump\Protobuf::RULE_REPEATED;
|
||||
$descriptor->addField($f);
|
||||
|
||||
// OPTIONAL INT64 split_count = 5
|
||||
|
@ -71,6 +80,31 @@ namespace Vitess\Proto\Vtgate {
|
|||
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
|
||||
$descriptor->addField($f);
|
||||
|
||||
// OPTIONAL INT64 num_rows_per_query_part = 6
|
||||
$f = new \DrSlump\Protobuf\Field();
|
||||
$f->number = 6;
|
||||
$f->name = "num_rows_per_query_part";
|
||||
$f->type = \DrSlump\Protobuf::TYPE_INT64;
|
||||
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
|
||||
$descriptor->addField($f);
|
||||
|
||||
// OPTIONAL ENUM algorithm = 7
|
||||
$f = new \DrSlump\Protobuf\Field();
|
||||
$f->number = 7;
|
||||
$f->name = "algorithm";
|
||||
$f->type = \DrSlump\Protobuf::TYPE_ENUM;
|
||||
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
|
||||
$f->reference = '\Vitess\Proto\Query\SplitQueryRequest\Algorithm';
|
||||
$descriptor->addField($f);
|
||||
|
||||
// OPTIONAL BOOL use_split_query_v2 = 8
|
||||
$f = new \DrSlump\Protobuf\Field();
|
||||
$f->number = 8;
|
||||
$f->name = "use_split_query_v2";
|
||||
$f->type = \DrSlump\Protobuf::TYPE_BOOL;
|
||||
$f->rule = \DrSlump\Protobuf::RULE_OPTIONAL;
|
||||
$descriptor->addField($f);
|
||||
|
||||
foreach (self::$__extensions as $cb) {
|
||||
$descriptor->addField($cb(), true);
|
||||
}
|
||||
|
@ -210,10 +244,11 @@ namespace Vitess\Proto\Vtgate {
|
|||
/**
|
||||
* Get <split_column> value
|
||||
*
|
||||
* @param int $idx
|
||||
* @return string
|
||||
*/
|
||||
public function getSplitColumn(){
|
||||
return $this->_get(4);
|
||||
public function getSplitColumn($idx = NULL){
|
||||
return $this->_get(4, $idx);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -222,8 +257,27 @@ namespace Vitess\Proto\Vtgate {
|
|||
* @param string $value
|
||||
* @return \Vitess\Proto\Vtgate\SplitQueryRequest
|
||||
*/
|
||||
public function setSplitColumn( $value){
|
||||
return $this->_set(4, $value);
|
||||
public function setSplitColumn( $value, $idx = NULL){
|
||||
return $this->_set(4, $value, $idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all elements of <split_column>
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getSplitColumnList(){
|
||||
return $this->_get(4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new element to <split_column>
|
||||
*
|
||||
* @param string $value
|
||||
* @return \Vitess\Proto\Vtgate\SplitQueryRequest
|
||||
*/
|
||||
public function addSplitColumn( $value){
|
||||
return $this->_add(4, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -262,6 +316,117 @@ namespace Vitess\Proto\Vtgate {
|
|||
public function setSplitCount( $value){
|
||||
return $this->_set(5, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if <num_rows_per_query_part> has a value
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasNumRowsPerQueryPart(){
|
||||
return $this->_has(6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear <num_rows_per_query_part> value
|
||||
*
|
||||
* @return \Vitess\Proto\Vtgate\SplitQueryRequest
|
||||
*/
|
||||
public function clearNumRowsPerQueryPart(){
|
||||
return $this->_clear(6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get <num_rows_per_query_part> value
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNumRowsPerQueryPart(){
|
||||
return $this->_get(6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set <num_rows_per_query_part> value
|
||||
*
|
||||
* @param int $value
|
||||
* @return \Vitess\Proto\Vtgate\SplitQueryRequest
|
||||
*/
|
||||
public function setNumRowsPerQueryPart( $value){
|
||||
return $this->_set(6, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if <algorithm> has a value
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasAlgorithm(){
|
||||
return $this->_has(7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear <algorithm> value
|
||||
*
|
||||
* @return \Vitess\Proto\Vtgate\SplitQueryRequest
|
||||
*/
|
||||
public function clearAlgorithm(){
|
||||
return $this->_clear(7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get <algorithm> value
|
||||
*
|
||||
* @return int - \Vitess\Proto\Query\SplitQueryRequest\Algorithm
|
||||
*/
|
||||
public function getAlgorithm(){
|
||||
return $this->_get(7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set <algorithm> value
|
||||
*
|
||||
* @param int - \Vitess\Proto\Query\SplitQueryRequest\Algorithm $value
|
||||
* @return \Vitess\Proto\Vtgate\SplitQueryRequest
|
||||
*/
|
||||
public function setAlgorithm( $value){
|
||||
return $this->_set(7, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if <use_split_query_v2> has a value
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasUseSplitQueryV2(){
|
||||
return $this->_has(8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear <use_split_query_v2> value
|
||||
*
|
||||
* @return \Vitess\Proto\Vtgate\SplitQueryRequest
|
||||
*/
|
||||
public function clearUseSplitQueryV2(){
|
||||
return $this->_clear(8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get <use_split_query_v2> value
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getUseSplitQueryV2(){
|
||||
return $this->_get(8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set <use_split_query_v2> value
|
||||
*
|
||||
* @param boolean $value
|
||||
* @return \Vitess\Proto\Vtgate\SplitQueryRequest
|
||||
*/
|
||||
public function setUseSplitQueryV2( $value){
|
||||
return $this->_set(8, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -204,13 +204,15 @@ class VTGateConn
|
|||
return new VTGateTx($this->client, $response->getSession());
|
||||
}
|
||||
|
||||
// TODO(erez): Migrate to SplitQueryV2 after it's stable.
|
||||
public function splitQuery(Context $ctx, $keyspace, $query, array $bind_vars, $split_column, $split_count)
|
||||
{
|
||||
$request = new Proto\Vtgate\SplitQueryRequest();
|
||||
$request->setKeyspace($keyspace);
|
||||
$request->setQuery(ProtoUtils::BoundQuery($query, $bind_vars));
|
||||
$request->setSplitColumn($split_column);
|
||||
$request->addSplitColumn($split_column);
|
||||
$request->setSplitCount($split_count);
|
||||
$request->setUseSplitQueryV2(false);
|
||||
if ($ctx->getCallerId()) {
|
||||
$request->setCallerId($ctx->getCallerId());
|
||||
}
|
||||
|
|
|
@ -288,15 +288,37 @@ message RollbackRequest {
|
|||
// RollbackResponse is the returned value from Rollback
|
||||
message RollbackResponse {}
|
||||
|
||||
// SplitQueryRequest is the payload for SplitQuery
|
||||
// SplitQueryRequest is the payload for SplitQuery sent by VTGate to a VTTablet.
|
||||
// See vtgate.SplitQueryRequest for more details.
|
||||
message SplitQueryRequest {
|
||||
vtrpc.CallerID effective_caller_id = 1;
|
||||
VTGateCallerID immediate_caller_id = 2;
|
||||
Target target = 3;
|
||||
|
||||
BoundQuery query = 4;
|
||||
string split_column = 5;
|
||||
repeated string split_column = 5;
|
||||
|
||||
// Exactly one of the following must be nonzero.
|
||||
int64 split_count = 6;
|
||||
int64 num_rows_per_query_part = 8;
|
||||
|
||||
int64 session_id = 7;
|
||||
|
||||
enum Algorithm {
|
||||
EQUAL_SPLITS = 0;
|
||||
FULL_SCAN = 1;
|
||||
}
|
||||
Algorithm algorithm = 9;
|
||||
|
||||
// Whether to use the new split-query code
|
||||
// that supports multiple split-columns and
|
||||
// the FULL_SCAN algorithm.
|
||||
// This is a temporary field which aids
|
||||
// in the migration of SplitQuery to the new
|
||||
// code.
|
||||
// TODO(erez): Remove this field after the migration
|
||||
// to the SplitQuery version 2.
|
||||
bool use_split_query_v2 = 10;
|
||||
}
|
||||
|
||||
// QuerySplit represents one query to execute on the tablet
|
||||
|
|
|
@ -41,6 +41,9 @@ message ExecuteRequest {
|
|||
|
||||
// not_in_transaction is deprecated and should not be used.
|
||||
bool not_in_transaction = 5;
|
||||
|
||||
// keyspace to target the query to.
|
||||
string keyspace = 6;
|
||||
}
|
||||
|
||||
// ExecuteResponse is the returned value from Execute.
|
||||
|
@ -345,6 +348,9 @@ message StreamExecuteRequest {
|
|||
|
||||
// tablet_type is the type of tablets that this query is targeted to.
|
||||
topodata.TabletType tablet_type = 3;
|
||||
|
||||
// keyspace to target the query to.
|
||||
string keyspace = 4;
|
||||
}
|
||||
|
||||
// StreamExecuteResponse is the returned value from StreamExecute.
|
||||
|
@ -480,6 +486,22 @@ message RollbackResponse {
|
|||
}
|
||||
|
||||
// SplitQueryRequest is the payload to SplitQuery.
|
||||
//
|
||||
// SplitQuery takes a "SELECT" query and generates a list of queries called
|
||||
// "query-parts". Each query-part consists of the original query with an
|
||||
// added WHERE clause that restricts the query-part to operate only on
|
||||
// rows whose values in the the columns listed in the "split_column" field
|
||||
// of the request (see below) are in a particular range.
|
||||
//
|
||||
// It is guaranteed that the set of rows obtained from
|
||||
// executing each query-part on a database snapshot
|
||||
// and merging (without deduping) the results is equal to the set of rows
|
||||
// obtained from executing the original query on the same snapshot
|
||||
// with the rows containing NULL values in any of the split_column's excluded.
|
||||
//
|
||||
// This is typically called by the MapReduce master when reading from Vitess.
|
||||
// There it's desirable that the sets of rows returned by the query-parts
|
||||
// have roughly the same size.
|
||||
message SplitQueryRequest {
|
||||
// caller_id identifies the caller. This is the effective caller ID,
|
||||
// set by the application to further identify the caller.
|
||||
|
@ -488,14 +510,77 @@ message SplitQueryRequest {
|
|||
// keyspace to target the query to.
|
||||
string keyspace = 2;
|
||||
|
||||
// query is the query and bind variables to produce splits for.
|
||||
// The query and bind variables to produce splits for.
|
||||
// The given query must be a simple query of the form
|
||||
// SELECT <cols> FROM <table> WHERE <filter>.
|
||||
// It must not contain subqueries nor any of the keywords
|
||||
// JOIN, GROUP BY, ORDER BY, LIMIT, DISTINCT.
|
||||
// Furthermore, <table> must be a single “concrete” table.
|
||||
// It cannot be a view.
|
||||
query.BoundQuery query = 3;
|
||||
|
||||
// split_column is an optional hint on the column to use to split the query.
|
||||
string split_column = 4;
|
||||
// Each generated query-part will be restricted to rows whose values
|
||||
// in the columns listed in this field are in a particular range.
|
||||
// The list of columns named here must be a prefix of the list of
|
||||
// columns defining some index or primary key of the table
|
||||
// referenced in 'query'. For many tables using the primary key columns
|
||||
// (in order) is sufficient and this is the default if this field is omitted.
|
||||
// See the comment on the 'algorithm' field for more restrictions and
|
||||
// information.
|
||||
repeated string split_column = 4;
|
||||
|
||||
// split_count describes how many splits we want for this query.
|
||||
// You can specify either an estimate of the number of query-parts to
|
||||
// generate or an estimate of the number of rows each query-part should
|
||||
// return.
|
||||
// Thus, exactly one of split_count or num_rows_per_query_part
|
||||
// should be nonzero.
|
||||
// The non-given parameter is calculated from the given parameter
|
||||
// using the formula: split_count * num_rows_per_query_pary = table_size,
|
||||
// where table_size is an approximation of the number of rows in the
|
||||
// table.
|
||||
// Note that if "split_count" is given it is regarded as an estimate.
|
||||
// The number of query-parts returned may differ slightly (in particular,
|
||||
// if it's not a whole multiple of the number of vitess shards).
|
||||
int64 split_count = 5;
|
||||
int64 num_rows_per_query_part = 6;
|
||||
|
||||
// The algorithm to use to split the query. The split algorithm is performed
|
||||
// on each database shard in parallel. The lists of query-parts generated
|
||||
// by the shards are merged and returned to the caller.
|
||||
// Two algorithms are supported:
|
||||
// EQUAL_SPLITS
|
||||
// If this algorithm is used then only one split_column is allowed.
|
||||
// Additionally, the split_column must have numeric type (integral or
|
||||
// floating point). The algorithm works by taking the interval [min, max],
|
||||
// where min and max are the minimum and maximum values of of the
|
||||
// split_column column in the table-shard, respectively, and
|
||||
// partitioning it into split_count sub-intervals of equal size.
|
||||
// The added WHERE clause of each query-part restricts that part to rows
|
||||
// whose value in split_column belongs to a particular sub-interval.
|
||||
// This is fast, but requires that the distribution of values of
|
||||
// split_column be uniform in [min, max], for the number of rows
|
||||
// returned by each query part to be roughly the same.
|
||||
// FULL_SCAN
|
||||
// If this algorithm is used then the split_column must be the primary key
|
||||
// columns (in order).
|
||||
// This algorithm performs a full-scan of the table-shard referenced
|
||||
// in 'query' to get "boundary" rows that are num_rows_per_query_part
|
||||
// apart when the table is ordered by the columns listed in
|
||||
// 'split_column'. It then restricts each query-part to the rows
|
||||
// located between two successive boundary rows.
|
||||
// This algorithm supports multiple split_column's of any type,
|
||||
// but is slower than EQUAL_SPLITS.
|
||||
query.SplitQueryRequest.Algorithm algorithm = 7;
|
||||
|
||||
// Whether to use the new split-query code
|
||||
// that supports multiple split-columns and
|
||||
// the FULL_SCAN algorithm.
|
||||
// This is a temporary field which aids
|
||||
// in the migration of SplitQuery to the new
|
||||
// code.
|
||||
// TODO(erez): Remove this field after the migration
|
||||
// to the SplitQuery version 2.
|
||||
bool use_split_query_v2 = 8;
|
||||
}
|
||||
|
||||
// SplitQueryResponse is the returned value from SplitQuery.
|
||||
|
@ -518,7 +603,8 @@ message SplitQueryResponse {
|
|||
// query is the query and bind variables to execute.
|
||||
query.BoundQuery query = 1;
|
||||
|
||||
// key_range_part is set if the query should be executed by ExecuteKeyRanges.
|
||||
// key_range_part is set if the query should be executed by
|
||||
// ExecuteKeyRanges.
|
||||
KeyRangePart key_range_part = 2;
|
||||
|
||||
// shard_part is set if the query should be executed by ExecuteShards.
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -126,7 +126,7 @@ vschema = '''{
|
|||
"Owner": "vt_music"
|
||||
}
|
||||
},
|
||||
"Classes": {
|
||||
"Tables": {
|
||||
"vt_user": {
|
||||
"ColVindexes": [
|
||||
{
|
||||
|
@ -203,29 +203,19 @@ vschema = '''{
|
|||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"Tables": {
|
||||
"vt_user": "vt_user",
|
||||
"vt_user2": "vt_user2",
|
||||
"vt_user_extra": "vt_user_extra",
|
||||
"vt_music": "vt_music",
|
||||
"vt_music_extra": "vt_music_extra",
|
||||
"join_user": "join_user",
|
||||
"join_user_extra": "join_user_extra"
|
||||
}
|
||||
},
|
||||
"lookup": {
|
||||
"Sharded": false,
|
||||
"Classes" : {
|
||||
"seq": {
|
||||
"Type": "Sequence"
|
||||
}
|
||||
},
|
||||
"Tables": {
|
||||
"vt_user_seq": "seq",
|
||||
"vt_music_seq": "seq",
|
||||
"music_user_map": "",
|
||||
"name_user2_map": ""
|
||||
"vt_user_seq": {
|
||||
"Type": "Sequence"
|
||||
},
|
||||
"vt_music_seq": {
|
||||
"Type": "Sequence"
|
||||
},
|
||||
"music_user_map": {},
|
||||
"name_user2_map": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,12 @@
|
|||
# It should be run from $VTTOP.
|
||||
|
||||
# Dependencies:
|
||||
# - PHP 5.3+
|
||||
# - PHP 5.5+
|
||||
# - PEAR
|
||||
# - protoc-gen-php: http://www.grpc.io/docs/installation/php.html
|
||||
# - gem (ruby)
|
||||
# - protoc (protobuf)
|
||||
# - protoc-gen-php:
|
||||
# https://github.com/grpc/grpc/tree/master/src/php#php-protobuf-compiler
|
||||
|
||||
set -e
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче