github.com/pingcap/tidb-lightning@v5.0.0-rc.0.20210428090220-84b649866577+incompatible/lightning/mydump/router_test.go (about)

     1  package mydump
     2  
     3  import (
     4  	"strings"
     5  
     6  	. "github.com/pingcap/check"
     7  	"github.com/pingcap/tidb-tools/pkg/filter"
     8  
     9  	"github.com/pingcap/tidb-lightning/lightning/config"
    10  )
    11  
    12  var _ = Suite(&testFileRouterSuite{})
    13  
    14  type testFileRouterSuite struct{}
    15  
    16  func (t *testFileRouterSuite) TestRouteParser(c *C) {
    17  	// valid rules
    18  	rules := []*config.FileRouteRule{
    19  		{Pattern: `^(?:[^/]*/)*([^/.]+)\.([^./]+)(?:\.[0-9]+)?\.(csv|sql)`, Schema: "$1", Table: "$2", Type: "$3"},
    20  		{Pattern: `^.+\.(csv|sql)`, Schema: "test", Table: "t", Type: "$1"},
    21  		{Pattern: `^(?:[^/]*/)*(?P<schema>[^/.]+)\.(?P<table>[^./]+)(?:\.(?P<key>[0-9]+))?\.(?P<type>csv|sql)(?:\.(?P<cp>[A-Za-z0-9]+))?$`, Schema: "$schema", Table: "$table", Type: "$type", Key: "$key", Compression: "$cp"},
    22  		{Pattern: `^(?:[^/]*/)*(?P<schema>[^/.]+)\.(?P<table>[^./]+)(?:\.([0-9]+))?\.(csv|sql)(?:\.(?P<cp>[A-Za-z0-9]+))?$`, Schema: "${schema}s", Table: "$table", Type: "${3}_0", Key: "$4", Compression: "$cp"},
    23  		{Pattern: `^(?:[^/]*/)*([^/.]+)\.(?P<table>[^./]+)(?:\.([0-9]+))?\.(csv|sql)(?:\.(?P<cp>[A-Za-z0-9]+))?$`, Schema: "${1}s", Table: "$table", Type: "${3}_0", Key: "$4", Compression: "$cp"},
    24  		{Pattern: `^(?:[^/]*/)*([^/.]+)\.([^./]+)(?:\.[0-9]+)?\.(csv|sql)`, Schema: "$1-schema", Table: "$1-table", Type: "$2"},
    25  	}
    26  	for _, r := range rules {
    27  		_, err := NewFileRouter([]*config.FileRouteRule{r})
    28  		c.Assert(err, IsNil)
    29  	}
    30  
    31  	// invalid rules
    32  	invalidRules := []*config.FileRouteRule{
    33  		{Pattern: `^(?:[^/]*/)*(?P<schema>\.(?P<table>[^./]+).*$`, Schema: "$test", Table: "$table"},
    34  		{Pattern: `^(?:[^/]*/)*(?P<schema>[^/.]+)\.(?P<table>[^./]+).*$`, Schema: "$schemas", Table: "$table"},
    35  		{Pattern: `^(?:[^/]*/)*([^/.]+)\.([^./]+)(?:\.[0-9]+)?\.(csv|sql)`, Schema: "$1", Table: "$2", Type: "$3", Key: "$4"},
    36  	}
    37  	for _, r := range invalidRules {
    38  		_, err := NewFileRouter([]*config.FileRouteRule{r})
    39  		c.Assert(err, NotNil)
    40  	}
    41  }
    42  
    43  func (t *testFileRouterSuite) TestInvalidRouteRule(c *C) {
    44  	rule := &config.FileRouteRule{}
    45  	rules := []*config.FileRouteRule{rule}
    46  	_, err := NewFileRouter(rules)
    47  	c.Assert(err, ErrorMatches, "`path` and `pattern` must not be both empty in \\[\\[mydumper.files\\]\\]")
    48  
    49  	rule.Pattern = `^(?:[^/]*/)*([^/.]+)\.(?P<table>[^./]+)(?:\.(?P<key>[0-9]+))?\.(?P<type>csv|sql)(?:\.(?P<cp>[A-Za-z0-9]+))?$`
    50  	_, err = NewFileRouter(rules)
    51  	c.Assert(err, ErrorMatches, "field 'type' match pattern can't be empty")
    52  
    53  	rule.Type = "$type"
    54  	_, err = NewFileRouter(rules)
    55  	c.Assert(err, ErrorMatches, "field 'schema' match pattern can't be empty")
    56  
    57  	rule.Schema = "$schema"
    58  	_, err = NewFileRouter(rules)
    59  	c.Assert(err, ErrorMatches, "invalid named capture '\\$schema'")
    60  
    61  	rule.Schema = "$1"
    62  	_, err = NewFileRouter(rules)
    63  	c.Assert(err, ErrorMatches, "field 'table' match pattern can't be empty")
    64  
    65  	rule.Table = "$table"
    66  	_, err = NewFileRouter(rules)
    67  	c.Assert(err, IsNil)
    68  
    69  	rule.Path = "/tmp/1.sql"
    70  	_, err = NewFileRouter(rules)
    71  	c.Assert(err, ErrorMatches, "can't set both `path` and `pattern` field in \\[\\[mydumper.files\\]\\]")
    72  }
    73  
    74  func (t *testFileRouterSuite) TestSingleRouteRule(c *C) {
    75  	rules := []*config.FileRouteRule{
    76  		{Pattern: `^(?:[^/]*/)*([^/.]+)\.(?P<table>[^./]+)(?:\.(?P<key>[0-9]+))?\.(?P<type>csv|sql)(?:\.(?P<cp>[A-Za-z0-9]+))?$`, Schema: "$1", Table: "$table", Type: "$type", Key: "$key", Compression: "$cp"},
    77  	}
    78  
    79  	r, err := NewFileRouter(rules)
    80  	c.Assert(err, IsNil)
    81  
    82  	inputOutputMap := map[string][]string{
    83  		"my_schema.my_table.sql":           {"my_schema", "my_table", "", "", "sql"},
    84  		"/test/123/my_schema.my_table.sql": {"my_schema", "my_table", "", "", "sql"},
    85  		"my_dir/my_schema.my_table.csv":    {"my_schema", "my_table", "", "", "csv"},
    86  		"my_schema.my_table.0001.sql":      {"my_schema", "my_table", "0001", "", "sql"},
    87  	}
    88  	for path, fields := range inputOutputMap {
    89  		res, err := r.Route(path)
    90  		c.Assert(err, IsNil)
    91  		compress, e := parseCompressionType(fields[3])
    92  		c.Assert(e, IsNil)
    93  		ty, e := parseSourceType(fields[4])
    94  		c.Assert(e, IsNil)
    95  		exp := &RouteResult{filter.Table{Schema: fields[0], Name: fields[1]}, fields[2], compress, ty}
    96  		c.Assert(res, DeepEquals, exp)
    97  	}
    98  
    99  	notMatchPaths := []string{
   100  		"my_table.sql",
   101  		"/schema/table.sql",
   102  		"my_schema.my_table.txt",
   103  		"my_schema.my_table.001.txt",
   104  		"my_schema.my_table.0001-002.sql",
   105  	}
   106  	for _, p := range notMatchPaths {
   107  		res, err := r.Route(p)
   108  		c.Assert(res, IsNil)
   109  		c.Assert(err, IsNil)
   110  	}
   111  
   112  	rule := &config.FileRouteRule{Pattern: `^(?:[^/]*/)*([^/.]+)\.(?P<table>[^./]+)(?:\.(?P<key>[0-9]+))?\.(?P<type>\w+)(?:\.(?P<cp>[A-Za-z0-9]+))?$`, Schema: "$1", Table: "$table", Type: "$type", Key: "$key", Compression: "$cp"}
   113  	r, err = NewFileRouter([]*config.FileRouteRule{rule})
   114  	c.Assert(err, IsNil)
   115  	c.Assert(r, NotNil)
   116  	invalidMatchPaths := []string{
   117  		"my_schema.my_table.sql.gz",
   118  		"my_schema.my_table.sql.rar",
   119  		"my_schema.my_table.txt",
   120  	}
   121  	for _, p := range invalidMatchPaths {
   122  		res, err := r.Route(p)
   123  		c.Assert(res, IsNil)
   124  		c.Assert(err, NotNil)
   125  	}
   126  }
   127  
   128  func (t *testFileRouterSuite) TestMultiRouteRule(c *C) {
   129  	// multi rule don't intersect with each other
   130  	rules := []*config.FileRouteRule{
   131  		{Pattern: `(?:[^/]*/)*([^/.]+)-schema-create\.sql`, Schema: "$1", Type: SchemaSchema},
   132  		{Pattern: `(?:[^/]*/)*([^/.]+)\.([^/.]+)-schema\.sql$`, Schema: "$1", Table: "$2", Type: TableSchema},
   133  		{Pattern: `(?:[^/]*/)*([^/.]+)\.([^/.]+)-schema-view\.sql$`, Schema: "$1", Table: "$2", Type: ViewSchema},
   134  		{Pattern: `^(?:[^/]*/)*(?P<schema>[^/.]+)\.(?P<table>[^./]+)(?:\.(?P<key>[0-9]+))?\.(?P<type>csv|sql)(?:\.(?P<cp>[A-Za-z0-9]+))?$`, Schema: "$schema", Table: "$table", Type: "$type", Key: "$key", Compression: "$cp"},
   135  	}
   136  
   137  	r, err := NewFileRouter(rules)
   138  	c.Assert(err, IsNil)
   139  
   140  	inputOutputMap := map[string][]string{
   141  		"test-schema-create.sql":           {"test", "", "", "", SchemaSchema},
   142  		"test.t-schema.sql":                {"test", "t", "", "", TableSchema},
   143  		"test.v1-schema-view.sql":          {"test", "v1", "", "", ViewSchema},
   144  		"my_schema.my_table.sql":           {"my_schema", "my_table", "", "", "sql"},
   145  		"/test/123/my_schema.my_table.sql": {"my_schema", "my_table", "", "", "sql"},
   146  		"my_dir/my_schema.my_table.csv":    {"my_schema", "my_table", "", "", "csv"},
   147  		"my_schema.my_table.0001.sql":      {"my_schema", "my_table", "0001", "", "sql"},
   148  		//"my_schema.my_table.0001.sql.gz":      {"my_schema", "my_table", "0001", "gz", "sql"},
   149  	}
   150  	for path, fields := range inputOutputMap {
   151  		res, err := r.Route(path)
   152  		c.Assert(err, IsNil)
   153  		if len(fields) == 0 {
   154  			c.Assert(res, IsNil)
   155  		} else {
   156  			compress, e := parseCompressionType(fields[3])
   157  			c.Assert(e, IsNil)
   158  			ty, e := parseSourceType(fields[4])
   159  			c.Assert(e, IsNil)
   160  			exp := &RouteResult{filter.Table{Schema: fields[0], Name: fields[1]}, fields[2], compress, ty}
   161  			c.Assert(res, DeepEquals, exp)
   162  		}
   163  	}
   164  
   165  	// multi rule don't intersect with each other
   166  	// add another rule that math same pattern with the third rule, the result should be no different
   167  	p := &config.FileRouteRule{Pattern: `^(?P<schema>[^/.]+)\.(?P<table>[^./]+)(?:\.(?P<key>[0-9]+))?\.(?P<type>csv|sql)(?:\.(?P<cp>[A-Za-z0-9]+))?$`, Schema: "test_schema", Table: "test_table", Type: "$type", Key: "$key", Compression: "$cp"}
   168  	rules = append(rules, p)
   169  	r, err = NewFileRouter(rules)
   170  	c.Assert(err, IsNil)
   171  	for path, fields := range inputOutputMap {
   172  		res, err := r.Route(path)
   173  		c.Assert(err, IsNil)
   174  		if len(fields) == 0 {
   175  			c.Assert(res, IsNil)
   176  		} else {
   177  			compress, e := parseCompressionType(fields[3])
   178  			c.Assert(e, IsNil)
   179  			ty, e := parseSourceType(fields[4])
   180  			c.Assert(e, IsNil)
   181  			exp := &RouteResult{filter.Table{Schema: fields[0], Name: fields[1]}, fields[2], compress, ty}
   182  			c.Assert(res, DeepEquals, exp)
   183  		}
   184  	}
   185  }
   186  
   187  func (t *testFileRouterSuite) TestRouteExpanding(c *C) {
   188  	rule := &config.FileRouteRule{
   189  		Pattern:     `^(?:[^/]*/)*(?P<schema>[^/.]+)\.(?P<table_name>[^./]+)(?:\.(?P<key>[0-9]+))?\.(?P<type>csv|sql)(?:\.(?P<cp>[A-Za-z0-9]+))?$`,
   190  		Schema:      "$schema",
   191  		Type:        "$type",
   192  		Key:         "$key",
   193  		Compression: "$cp",
   194  	}
   195  	path := "db.table.001.sql"
   196  	tablePatternResMap := map[string]string{
   197  		"$schema":             "db",
   198  		"$table_name":         "table",
   199  		"$schema.$table_name": "db.table",
   200  		"${1}":                "db",
   201  		"${1}_$table_name":    "db_table",
   202  		"${2}.schema":         "table.schema",
   203  		"$${2}":               "${2}",
   204  		"$$table_name":        "$table_name",
   205  		"$table_name-123":     "table-123",
   206  		"$$12$1$schema":       "$12dbdb",
   207  		"${table_name}$$2":    "table$2",
   208  		"${table_name}$$":     "table$",
   209  		"{1}$$":               "{1}$",
   210  		"my_table":            "my_table",
   211  	}
   212  
   213  	for pat, value := range tablePatternResMap {
   214  		rule.Table = pat
   215  		router, err := NewFileRouter([]*config.FileRouteRule{rule})
   216  		c.Assert(err, IsNil)
   217  		res, err := router.Route(path)
   218  		c.Assert(err, IsNil)
   219  		c.Assert(res, NotNil)
   220  		c.Assert(res.Name, Equals, value)
   221  	}
   222  
   223  	invalidPatterns := []string{"$1_$schema", "$schema_$table_name", "$6"}
   224  	for _, pat := range invalidPatterns {
   225  		rule.Table = pat
   226  		_, err := NewFileRouter([]*config.FileRouteRule{rule})
   227  		c.Assert(err, NotNil)
   228  	}
   229  }
   230  
   231  func (t *testFileRouterSuite) TestRouteWithPath(c *C) {
   232  	fileName := "myschema.(my_table$1).000.sql"
   233  	rule := &config.FileRouteRule{
   234  		Path:   fileName,
   235  		Schema: "schema",
   236  		Table:  "my_table$1",
   237  		Type:   "sql",
   238  		Key:    "$key",
   239  	}
   240  	r := *rule
   241  	router, err := NewFileRouter([]*config.FileRouteRule{&r})
   242  	c.Assert(err, IsNil)
   243  	res, err := router.Route(fileName)
   244  	c.Assert(err, IsNil)
   245  	c.Assert(res, NotNil)
   246  	c.Assert(res.Schema, Equals, rule.Schema)
   247  	c.Assert(res.Name, Equals, rule.Table)
   248  	ty, _ := parseSourceType(rule.Type)
   249  	c.Assert(res.Type, Equals, ty)
   250  	c.Assert(res.Key, Equals, rule.Key)
   251  
   252  	// replace all '.' by '-', if with plain regex pattern, will still match
   253  	res, err = router.Route(strings.ReplaceAll(fileName, ".", "-"))
   254  	c.Assert(err, IsNil)
   255  	c.Assert(res, IsNil)
   256  }