func (c *Config) resolveRules(canal *canal.Canal) (map[string]*Rule, error) { ruleMap, wildtables, err := c.parseSource(canal) if err != nil { return nil, err } if c.Rules != nil { // then, set custom mapping rule for _, rule := range c.Rules { if len(rule.Schema) == 0 { return nil, errors.Errorf("empty schema not allowed for rule") } if regexp.QuoteMeta(rule.Table) != rule.Table { //wildcard table tables, ok := wildtables[ruleKey(rule.Schema, rule.Table)] if !ok { return nil, errors.Errorf("wildcard table for %s.%s is not defined in source", rule.Schema, rule.Table) } if len(rule.Index) == 0 { return nil, errors.Errorf("wildcard table rule %s.%s must have a index, can not empty", rule.Schema, rule.Table) } rule.Prepare() for _, table := range tables { rr := ruleMap[ruleKey(rule.Schema, table)] rr.Index = rule.Index rr.Type = rule.Type rr.Parent = rule.Parent rr.FieldMapping = rule.FieldMapping } } else { key := ruleKey(rule.Schema, rule.Table) if _, ok := ruleMap[key]; !ok { return nil, errors.Errorf("rule %s, %s not defined in source", rule.Schema, rule.Table) } rule.Prepare() ruleMap[key] = rule } } } for _, rule := range ruleMap { if rule.TableInfo, err = canal.GetTable(rule.Schema, rule.Table); err != nil { return nil, err } // table must have a PK for one column, multi columns may be supported later. if len(rule.TableInfo.PKColumns) != 1 { return nil, errors.Errorf("%s.%s must have a PK for a column", rule.Schema, rule.Table) } } return ruleMap, nil }
func (c *Config) parseSource(canal *canal.Canal) (map[string]*Rule, map[string][]string, error) { ruleMap := make(map[string]*Rule) wildTables := make(map[string][]string, len(c.Sources)) // first, check sources for _, s := range c.Sources { for _, table := range s.Tables { if len(s.Schema) == 0 { return nil, nil, errors.Errorf("empty schema not allowed for source") } if regexp.QuoteMeta(table) != table { if _, ok := wildTables[ruleKey(s.Schema, table)]; ok { return nil, nil, errors.Errorf("duplicate wildcard table defined for %s.%s", s.Schema, table) } tables := []string{} sql := fmt.Sprintf(`SELECT table_name FROM information_schema.tables WHERE table_name RLIKE "%s" AND table_schema = "%s";`, table, s.Schema) res, err := canal.Execute(sql) if err != nil { return nil, nil, err } for i := 0; i < res.Resultset.RowNumber(); i++ { f, _ := res.GetString(i, 0) err := c.newRule(ruleMap, s.Schema, f) if err != nil { return nil, nil, err } tables = append(tables, f) } wildTables[ruleKey(s.Schema, table)] = tables } else { err := c.newRule(ruleMap, s.Schema, table) if err != nil { return nil, nil, err } } } } if len(ruleMap) == 0 { return nil, nil, errors.Errorf("no source data defined") } return ruleMap, wildTables, nil }