github.com/team-ide/go-dialect@v1.9.20/worker/task_sync.go (about)

     1  package worker
     2  
     3  import (
     4  	"database/sql"
     5  	"errors"
     6  	"fmt"
     7  	"github.com/team-ide/go-dialect/dialect"
     8  	"strings"
     9  )
    10  
    11  func NewTaskSync(sourceDB *sql.DB, sourceDialect dialect.Dialect, targetDb *sql.DB, targetDialect dialect.Dialect, newDb func(owner *TaskSyncOwner) (db *sql.DB, err error), taskSyncParam *TaskSyncParam) (res *taskSync) {
    12  	task := &Task{
    13  		dia:        sourceDialect,
    14  		db:         sourceDB,
    15  		onProgress: taskSyncParam.OnProgress,
    16  	}
    17  	res = &taskSync{
    18  		Task:          task,
    19  		targetDialect: targetDialect,
    20  		targetDb:      targetDb,
    21  		TaskSyncParam: taskSyncParam,
    22  		newDb:         newDb,
    23  	}
    24  	task.do = res.do
    25  	return
    26  }
    27  
    28  type TaskSyncParam struct {
    29  	Owners []*TaskSyncOwner `json:"owners"`
    30  
    31  	BatchNumber           int  `json:"batchNumber"`
    32  	SyncStruct            bool `json:"syncStruct"`
    33  	SyncData              bool `json:"syncData"`
    34  	ErrorContinue         bool `json:"errorContinue"`
    35  	OwnerCreateIfNotExist bool `json:"ownerCreateIfNotExist"`
    36  
    37  	FormatIndexName func(ownerName string, tableName string, index *dialect.IndexModel) string `json:"-"`
    38  	OnProgress      func(progress *TaskProgress)                                               `json:"-"`
    39  }
    40  
    41  type TaskSyncOwner struct {
    42  	SourceName     string           `json:"sourceName"`
    43  	TargetName     string           `json:"targetName"`
    44  	SkipTableNames []string         `json:"skipTableNames"`
    45  	Tables         []*TaskSyncTable `json:"tables"`
    46  	Username       string           `json:"username"`
    47  	Password       string           `json:"password"`
    48  }
    49  
    50  type TaskSyncTable struct {
    51  	SourceName string            `json:"sourceName"`
    52  	TargetName string            `json:"targetName"`
    53  	Columns    []*TaskSyncColumn `json:"columns"`
    54  }
    55  
    56  type TaskSyncColumn struct {
    57  	SourceName string `json:"sourceName"`
    58  	TargetName string `json:"targetName"`
    59  }
    60  
    61  type taskSync struct {
    62  	*Task
    63  	*TaskSyncParam
    64  	targetDialect dialect.Dialect
    65  	targetDb      *sql.DB
    66  	newDb         func(owner *TaskSyncOwner) (db *sql.DB, err error)
    67  }
    68  
    69  func (this_ *taskSync) do() (err error) {
    70  
    71  	defer func() {
    72  		if e := recover(); e != nil {
    73  			err = errors.New(fmt.Sprint(e))
    74  		}
    75  	}()
    76  
    77  	owners := this_.Owners
    78  	if len(owners) == 0 {
    79  		return
    80  	}
    81  	this_.countIncr(&this_.OwnerCount, len(owners))
    82  	for _, owner := range owners {
    83  		var success bool
    84  		success, err = this_.syncOwner(owner)
    85  		if success {
    86  			this_.countIncr(&this_.OwnerSuccessCount, 1)
    87  		} else {
    88  			this_.countIncr(&this_.OwnerErrorCount, 1)
    89  		}
    90  		if err != nil {
    91  			return
    92  		}
    93  	}
    94  
    95  	return
    96  }
    97  
    98  func (this_ *taskSync) syncOwner(owner *TaskSyncOwner) (success bool, err error) {
    99  	progress := &TaskProgress{
   100  		Title: "同步[" + owner.SourceName + "]",
   101  	}
   102  	defer func() {
   103  		if e := recover(); e != nil {
   104  			err = errors.New(fmt.Sprint(e))
   105  		}
   106  		if err != nil {
   107  			progress.Error = err.Error()
   108  			if progress.OnError != nil {
   109  				progress.OnError(err)
   110  			}
   111  		}
   112  
   113  		if this_.ErrorContinue {
   114  			err = nil
   115  		}
   116  	}()
   117  
   118  	this_.addProgress(progress)
   119  
   120  	ownerOne, err := OwnerSelect(this_.db, this_.dia, this_.Param, owner.SourceName)
   121  	if err != nil {
   122  		//fmt.Println("task sync syncOwner OwnerSelect owner:", owner.SourceName, " error:", err.Error())
   123  		return
   124  	}
   125  	if ownerOne == nil {
   126  		err = errors.New("source db owner [" + owner.SourceName + "] is not exist")
   127  		return
   128  	}
   129  
   130  	tables := owner.Tables
   131  
   132  	if len(tables) == 0 {
   133  		var list []*dialect.TableModel
   134  		list, err = TablesSelect(this_.db, this_.dia, this_.Param, owner.SourceName)
   135  		if err != nil {
   136  			//fmt.Println("task sync syncOwner TablesSelect owner:", owner.SourceName, " error:", err.Error())
   137  			return
   138  		}
   139  		progress.Infos = append(progress.Infos, fmt.Sprintf("owner[%s] table size[%d]", owner.SourceName, len(list)))
   140  		for _, one := range list {
   141  			tables = append(tables, &TaskSyncTable{
   142  				SourceName: one.TableName,
   143  			})
   144  		}
   145  	}
   146  	targetOwnerName := owner.TargetName
   147  	if targetOwnerName == "" {
   148  		targetOwnerName = owner.SourceName
   149  	}
   150  
   151  	targetOwnerOne, err := OwnerSelect(this_.targetDb, this_.targetDialect, this_.Param, targetOwnerName)
   152  	if err != nil {
   153  		return
   154  	}
   155  	if targetOwnerOne == nil {
   156  		if !this_.OwnerCreateIfNotExist {
   157  			err = errors.New("target db owner [" + targetOwnerName + "] is not exist")
   158  			return
   159  		}
   160  		this_.addProgress(&TaskProgress{
   161  			Title: "同步[" + targetOwnerName + "] 不存在,创建",
   162  		})
   163  		_, err = OwnerCreate(this_.targetDb, this_.targetDialect, this_.Param, &dialect.OwnerModel{
   164  			OwnerName:             targetOwnerName,
   165  			OwnerPassword:         owner.Password,
   166  			OwnerCharacterSetName: "utf8mb4",
   167  		})
   168  		if err != nil {
   169  			return
   170  		}
   171  	}
   172  
   173  	workDb, err := this_.newDb(owner)
   174  	if err != nil {
   175  		return
   176  	}
   177  
   178  	this_.countIncr(&this_.TableCount, len(tables))
   179  	for _, table := range tables {
   180  
   181  		if len(owner.SkipTableNames) > 0 {
   182  			var skip bool
   183  			for _, skipTableName := range owner.SkipTableNames {
   184  				if strings.EqualFold(table.SourceName, skipTableName) {
   185  					skip = true
   186  				}
   187  			}
   188  			if skip {
   189  				this_.countIncr(&this_.TableSuccessCount, 1)
   190  				continue
   191  			}
   192  		}
   193  
   194  		var success_ bool
   195  		success_, err = this_.syncTable(workDb, owner.SourceName, table.SourceName, owner.TargetName, table.TargetName)
   196  		if success_ {
   197  			this_.countIncr(&this_.TableSuccessCount, 1)
   198  		} else {
   199  			this_.countIncr(&this_.TableErrorCount, 1)
   200  		}
   201  		if err != nil {
   202  			return
   203  		}
   204  	}
   205  	success = true
   206  
   207  	return
   208  }
   209  
   210  func (this_ *taskSync) syncTable(workDb *sql.DB, sourceOwnerName string, sourceTableName string, targetOwnerName string, targetTableName string) (success bool, err error) {
   211  	if targetOwnerName == "" {
   212  		targetOwnerName = sourceOwnerName
   213  	}
   214  	if targetTableName == "" {
   215  		targetTableName = sourceTableName
   216  	}
   217  	progress := &TaskProgress{
   218  		Title: "同步[" + sourceOwnerName + "." + sourceTableName + "] 到 [" + targetOwnerName + "." + targetTableName + "]",
   219  	}
   220  	defer func() {
   221  		if e := recover(); e != nil {
   222  			err = errors.New(fmt.Sprint(e))
   223  		}
   224  		if err != nil {
   225  			progress.Error = err.Error()
   226  			if progress.OnError != nil {
   227  				progress.OnError(err)
   228  			}
   229  		}
   230  
   231  		if this_.ErrorContinue {
   232  			err = nil
   233  		}
   234  	}()
   235  
   236  	this_.addProgress(progress)
   237  
   238  	newTableDetail, err := TableDetail(this_.db, this_.dia, this_.Param, sourceOwnerName, sourceTableName, false)
   239  	if err != nil {
   240  		return
   241  	}
   242  	if newTableDetail == nil {
   243  		err = errors.New("source db table [" + sourceOwnerName + "." + sourceTableName + "] is not exist")
   244  		return
   245  	}
   246  	oldTableDetail, err := TableDetail(this_.targetDb, this_.targetDialect, this_.Param, targetOwnerName, targetTableName, false)
   247  	if err != nil {
   248  		return
   249  	}
   250  	if oldTableDetail == nil {
   251  		oldTableDetail = &dialect.TableModel{}
   252  	}
   253  	oldTableDetail.OwnerName = targetOwnerName
   254  	oldTableDetail.TableName = targetTableName
   255  	if this_.SyncStruct {
   256  		err = this_.syncTableSyncStructure(workDb, newTableDetail, oldTableDetail)
   257  		if err != nil {
   258  			return
   259  		}
   260  	}
   261  	if this_.SyncData {
   262  		err = this_.syncTableSyncData(workDb, newTableDetail, oldTableDetail)
   263  		if err != nil {
   264  			return
   265  		}
   266  	}
   267  	success = true
   268  	return
   269  }
   270  
   271  func (this_ *taskSync) syncTableSyncStructure(workDb *sql.DB, newTableDetail *dialect.TableModel, oldTableDetail *dialect.TableModel) (err error) {
   272  
   273  	progress := &TaskProgress{
   274  		Title: "同步表结构[" + newTableDetail.OwnerName + "." + newTableDetail.TableName + "] 到 [" + oldTableDetail.OwnerName + "." + oldTableDetail.TableName + "]",
   275  	}
   276  	defer func() {
   277  		if e := recover(); e != nil {
   278  			err = errors.New(fmt.Sprint(e))
   279  		}
   280  		if err != nil {
   281  			progress.Error = err.Error()
   282  			if progress.OnError != nil {
   283  				progress.OnError(err)
   284  			}
   285  		}
   286  
   287  		if this_.ErrorContinue {
   288  			err = nil
   289  		}
   290  	}()
   291  
   292  	this_.addProgress(progress)
   293  
   294  	if this_.FormatIndexName != nil {
   295  		for _, index := range newTableDetail.IndexList {
   296  			index.IndexName = this_.FormatIndexName(oldTableDetail.OwnerName, oldTableDetail.TableName, index)
   297  		}
   298  	}
   299  	if len(oldTableDetail.ColumnList) == 0 {
   300  		newTableDetail.TableName = oldTableDetail.TableName
   301  		err = TableCreate(workDb, this_.targetDialect, this_.Param, oldTableDetail.OwnerName, newTableDetail)
   302  		if err != nil {
   303  			return
   304  		}
   305  		return
   306  	} else {
   307  		err = TableUpdate(workDb, this_.targetDialect, oldTableDetail, this_.dia, newTableDetail)
   308  		if err != nil {
   309  			return
   310  		}
   311  	}
   312  	return
   313  }
   314  
   315  func (this_ *taskSync) syncTableSyncData(workDb *sql.DB, newTableDetail *dialect.TableModel, oldTableDetail *dialect.TableModel) (err error) {
   316  
   317  	progress := &TaskProgress{
   318  		Title: "同步表数据[" + newTableDetail.OwnerName + "." + newTableDetail.TableName + "] 到 [" + oldTableDetail.OwnerName + "." + oldTableDetail.TableName + "]",
   319  	}
   320  	defer func() {
   321  		if e := recover(); e != nil {
   322  			err = errors.New(fmt.Sprint(e))
   323  		}
   324  		if err != nil {
   325  			progress.Error = err.Error()
   326  			if progress.OnError != nil {
   327  				progress.OnError(err)
   328  			}
   329  		}
   330  
   331  		if this_.ErrorContinue {
   332  			err = nil
   333  		}
   334  	}()
   335  
   336  	this_.addProgress(progress)
   337  
   338  	selectSqlInfo := "SELECT "
   339  	var columnNames []string
   340  	for _, one := range newTableDetail.ColumnList {
   341  		columnNames = append(columnNames, one.ColumnName)
   342  	}
   343  	selectSqlInfo += this_.dia.ColumnNamesPack(this_.Param, columnNames)
   344  	selectSqlInfo += " FROM "
   345  	selectSqlInfo += this_.dia.OwnerTablePack(this_.Param, newTableDetail.OwnerName, newTableDetail.TableName)
   346  
   347  	countSql, err := dialect.FormatCountSql(selectSqlInfo)
   348  	if err != nil {
   349  		return
   350  	}
   351  	totalCount, err := DoQueryCount(this_.db, countSql, nil)
   352  	if err != nil {
   353  		return
   354  	}
   355  
   356  	this_.countIncr(&this_.DataCount, totalCount)
   357  	batchNumber := this_.BatchNumber
   358  	if batchNumber <= 0 {
   359  		batchNumber = 100
   360  	}
   361  	var pageSize = batchNumber
   362  	var pageNo = 1
   363  
   364  	var dataList []map[string]interface{}
   365  	var columnList = newTableDetail.ColumnList
   366  	if len(oldTableDetail.ColumnList) > 0 {
   367  		columnList = oldTableDetail.ColumnList
   368  	}
   369  	for {
   370  
   371  		if this_.IsStop {
   372  			return
   373  		}
   374  		pageSql := this_.dia.PackPageSql(selectSqlInfo, pageSize, pageNo)
   375  		dataList, err = DoQuery(this_.db, pageSql, nil)
   376  		if err != nil {
   377  			return
   378  		}
   379  		pageNo += 1
   380  		dataListCount := len(dataList)
   381  		this_.countIncr(&this_.DataReadyCount, dataListCount)
   382  		if dataListCount == 0 {
   383  			break
   384  		}
   385  		var success bool
   386  		success, err = this_.insertDataList(workDb, dataList, oldTableDetail.OwnerName, oldTableDetail.TableName, columnList)
   387  		if success {
   388  			this_.countIncr(&this_.DataSuccessCount, dataListCount)
   389  		} else {
   390  			this_.countIncr(&this_.DataErrorCount, dataListCount)
   391  		}
   392  		if err != nil {
   393  			return
   394  		}
   395  		if dataListCount == 0 {
   396  			break
   397  		}
   398  	}
   399  
   400  	return
   401  }
   402  
   403  func (this_ *taskSync) insertDataList(workDb *sql.DB, dataList []map[string]interface{}, targetOwnerName string, targetTableName string, columnList []*dialect.ColumnModel) (success bool, err error) {
   404  
   405  	progress := &TaskProgress{
   406  		Title: "插入数据[" + targetOwnerName + "." + targetTableName + "]",
   407  	}
   408  	defer func() {
   409  		if e := recover(); e != nil {
   410  			err = errors.New(fmt.Sprint(e))
   411  		}
   412  		if err != nil {
   413  			progress.Error = err.Error()
   414  			if progress.OnError != nil {
   415  				progress.OnError(err)
   416  			}
   417  		}
   418  
   419  		if this_.ErrorContinue {
   420  			err = nil
   421  		}
   422  	}()
   423  
   424  	this_.addProgress(progress)
   425  
   426  	_, _, batchSqlList, batchValuesList, err := this_.targetDialect.DataListInsertSql(this_.Param, targetOwnerName, targetTableName, columnList, dataList)
   427  	if err != nil {
   428  		return
   429  	}
   430  	var errSql string
   431  	_, errSql, _, err = DoExecs(workDb, batchSqlList, batchValuesList)
   432  	if err != nil {
   433  		if errSql != "" {
   434  			err = errors.New("sql:" + errSql + " exec error," + err.Error())
   435  		}
   436  		return
   437  	}
   438  	success = true
   439  	return
   440  }