func (g *generator) runHeader() { defer log.WhenDone().Info("Stats", "Package", g.tts.Package, "Step", "RunHeader") type Table struct { Name string TableName string } data := struct { Package, Tick string HasTypeCodeValueTables bool Tables []Table }{ Package: g.tts.Package, Tick: "`", HasTypeCodeValueTables: len(g.eavValueTables) > 0, } for _, table := range g.tables { data.Tables = append(data.Tables, Table{ Name: g.getConsistentName(table), TableName: g.getMagento2TableName(table), }) } g.appendToFile(tpl.Header, data, nil) }
func (g *generator) initTables() { defer log.WhenDone().Info("Stats", "Package", g.tts.Package, "Step", "InitTables") var err error g.tables, err = codegen.GetTables(g.dbrConn.NewSession(), codegen.ReplaceTablePrefix(g.tts.SQLQuery)) codegen.LogFatal(err) if len(g.tts.EntityTypeCodes) > 0 && g.tts.EntityTypeCodes[0] != "" { g.eavValueTables, err = codegen.GetEavValueTables(g.dbrConn, g.tts.EntityTypeCodes) codegen.LogFatal(err) for _, vTables := range g.eavValueTables { for t := range vTables { if false == isDuplicate(g.tables, t) { g.tables = append(g.tables, t) } } } } if g.tts.GenericsWhiteList == "" { return // do nothing because nothing defined, neither custom SQL nor to copy from SQLQuery field } if false == dbr.Stmt.IsSelect(g.tts.GenericsWhiteList) { // copy result from tables because select key word not found g.whiteListTables = g.tables return } g.whiteListTables, err = codegen.GetTables(g.dbrConn.NewSession(), codegen.ReplaceTablePrefix(g.tts.GenericsWhiteList)) codegen.LogFatal(err) }
func (g *generator) runTable() { defer log.WhenDone().Info("Stats", "Package", g.tts.Package, "Step", "RunTable") type OneTable struct { Package string Tick string Name string TableName string Struct string Slice string Table string GoColumns codegen.Columns Columns csdb.Columns MethodRecvPrefix string FindByPk string } for _, table := range g.tables { columns, err := codegen.GetColumns(g.dbrConn.DB, table) codegen.LogFatal(err) codegen.LogFatal(columns.MapSQLToGoDBRType()) name := g.getConsistentName(table) data := OneTable{ Package: g.tts.Package, Tick: "`", Name: name, TableName: g.getMagento2TableName(table), // original table name! Struct: TypePrefix + name, // getTableConstantName Slice: TypePrefix + name + "Slice", // getTableConstantName Table: table, GoColumns: columns, Columns: columns.CopyToCSDB(), } if data.Columns.PrimaryKeys().Len() > 0 { data.FindByPk = "FindBy" + utils.UnderscoreCamelize(data.Columns.PrimaryKeys().JoinFields("_")) } tplFuncs := template.FuncMap{ "typePrefix": func(name string) string { // if the method already exists in package then add the prefix parent // to avoid duplicate function names. search := data.Slice + name if g.existingMethodSets.has(search) { return MethodRecvPrefix + name } return name }, "findBy": findBy, "dbrType": dbrType, } g.appendToFile(g.getGenericTemplate(table), data, tplFuncs) } }
func detectMagentoVersion(dbrSess dbr.SessionRunner) (MageOne, MageTwo bool) { defer log.WhenDone().Info("Stats", "Package", "DetectMagentoVersion") allTables, err := codegen.GetTables(dbrSess) codegen.LogFatal(err) MageOne, MageTwo = utils.MagentoVersion(codegen.TablePrefix, allTables) if MageOne == MageTwo { codegen.LogFatal(errors.New("Cannot detect your Magento version")) } return }
func (g *generator) runEAValueTables() { if len(g.eavValueTables) == 0 { return } defer log.WhenDone().Info("Stats", "Package", g.tts.Package, "Step", "RunEAValueTables") data := struct { TypeCodeValueTables codegen.TypeCodeValueTable }{ TypeCodeValueTables: g.eavValueTables, } g.appendToFile(tpl.EAValueStructure, data, nil) }
func (g *generator) run() { defer log.WhenDone().Info("Stats", "Package", g.tts.Package) defer g.wg.Done() g.analyzePackage() var err error g.outfile, err = os.OpenFile(g.tts.OutputFile.String(), os.O_APPEND|os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) codegen.LogFatal(err) g.appendToFile(tpl.Copy, struct{ Package string }{Package: g.tts.Package}, nil) g.initTables() g.runHeader() g.runTable() g.runEAValueTables() codegen.LogFatal(g.outfile.Close()) }
// runCodec generates the codecs to be used later in JSON or msgpack or etc func runCodec(pkg, outfile, readfile string) { defer log.WhenDone().Info("Stats", "Package", pkg, "Step", "runCodec") if err := codecgen.Generate( outfile, // outfile "", // buildTag codecgen.GenCodecPath, false, // use unsafe "", regexp.MustCompile(TypePrefix+".*"), // Prefix of generated structs and slices true, // delete temp files readfile, // read from file ); err != nil { fmt.Println("codecgen.Generate Error:") codegen.LogFatal(err) } }
// analyzePackage extracts from all types the method receivers and type names. If we found existing // functions we will add a MethodRecvPrefix to the generated functions to avoid conflicts. func (g *generator) analyzePackage() { defer log.WhenDone().Info("Stats", "Package", g.tts.Package, "Step", "AnalyzePackage") fset := token.NewFileSet() path := filepath.Dir(g.tts.OutputFile.String()) pkgs, err := parser.ParseDir(fset, path, nil, parser.AllErrors) codegen.LogFatal(err) var astPkg *ast.Package var ok bool if astPkg, ok = pkgs[g.tts.Package]; !ok { fmt.Printf("Package %s not found in path %s. Skipping.", g.tts.Package, path) return } for fName, astFile := range astPkg.Files { if fName == g.tts.OutputFile.String() { // skip the generated file or we have recursion 8-) continue } ast.Inspect(astFile, func(n ast.Node) bool { switch stmt := n.(type) { case *ast.FuncDecl: if stmt.Recv != nil { // we have a method receiver and not a normal function switch t := stmt.Recv.List[0].Type.(type) { case *ast.Ident: // non-pointer-type if strings.Index(t.Name, TypePrefix) == 0 { g.existingMethodSets.add(t.Name + stmt.Name.Name) // e.g.: TableWebsiteSliceLoad where Load is the function name } case *ast.StarExpr: // pointer-type switch t2 := t.X.(type) { case *ast.Ident: if strings.Index(t2.Name, TypePrefix) == 0 { g.existingMethodSets.add(t2.Name + stmt.Name.Name) // e.g.: *TableWebsiteSliceLoad where Load is the function name } } } } } return true }) } }
func main() { defer log.WhenDone().Info("Stats") dbc, err := csdb.Connect() codegen.LogFatal(err) defer dbc.Close() var wg sync.WaitGroup mageV1, mageV2 := detectMagentoVersion(dbc.NewSession()) for _, tStruct := range codegen.ConfigTableToStruct { go newGenerator(tStruct, dbc, &wg).setMagentoVersion(mageV1, mageV2).run() } wg.Wait() for _, ts := range codegen.ConfigTableToStruct { // due to a race condition the codec generator must run after the newGenerator() calls runCodec(ts.Package, ts.OutputFile.AppendName("_codec").String(), ts.OutputFile.String()) } }
// GetTables returns all tables from a database. AndWhere can be optionally applied. // Only first index (0) will be added. func GetTables(dbrSess dbr.SessionRunner, sql ...string) ([]string, error) { qry := "SHOW TABLES" if len(sql) > 0 && sql[0] != "" { if false == dbr.Stmt.IsSelect(sql[0]) { qry = qry + " LIKE '" + sql[0] + "'" } else { qry = sql[0] } } if PkgLog.IsInfo() { // this if reduces 9 allocs ... defer log.WhenDone(PkgLog).Info("Stats", "Package", "codegen", "Step", "GetTables", "query", qry) } sb := dbrSess.SelectBySql(qry) query, args, err := sb.ToSql() if err != nil { return nil, errgo.Mask(err) } rows, err := sb.Query(query, args...) if err != nil { return nil, errgo.Mask(err) } defer rows.Close() var tableName string var tableNames = make([]string, 0, 200) for rows.Next() { if err := rows.Scan(&tableName); err != nil { return nil, errgo.Mask(err) } tableNames = append(tableNames, tableName) } if err = rows.Err(); err != nil { return nil, errgo.Mask(err) } return tableNames, nil }