github.com/kotovmak/go-admin@v1.1.1/plugins/admin/modules/table/default.go (about)

     1  package table
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"html/template"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"strconv"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/kotovmak/go-admin/modules/config"
    15  
    16  	"github.com/kotovmak/go-admin/modules/db"
    17  	"github.com/kotovmak/go-admin/modules/db/dialect"
    18  	errs "github.com/kotovmak/go-admin/modules/errors"
    19  	"github.com/kotovmak/go-admin/modules/language"
    20  	"github.com/kotovmak/go-admin/modules/logger"
    21  	"github.com/kotovmak/go-admin/plugins/admin/modules"
    22  	"github.com/kotovmak/go-admin/plugins/admin/modules/constant"
    23  	"github.com/kotovmak/go-admin/plugins/admin/modules/form"
    24  	"github.com/kotovmak/go-admin/plugins/admin/modules/paginator"
    25  	"github.com/kotovmak/go-admin/plugins/admin/modules/parameter"
    26  	"github.com/kotovmak/go-admin/template/types"
    27  )
    28  
    29  // DefaultTable is an implementation of table.Table
    30  type DefaultTable struct {
    31  	*BaseTable
    32  	connectionDriver     string
    33  	connectionDriverMode string
    34  	connection           string
    35  	sourceURL            string
    36  	getDataFun           GetDataFun
    37  
    38  	dbObj db.Connection
    39  }
    40  
    41  type GetDataFun func(params parameter.Parameters) ([]map[string]interface{}, int)
    42  
    43  func NewDefaultTable(cfgs ...Config) Table {
    44  
    45  	var cfg Config
    46  
    47  	if len(cfgs) > 0 && cfgs[0].PrimaryKey.Name != "" {
    48  		cfg = cfgs[0]
    49  	} else {
    50  		cfg = DefaultConfig()
    51  	}
    52  
    53  	return &DefaultTable{
    54  		BaseTable: &BaseTable{
    55  			Info:           types.NewInfoPanel(cfg.PrimaryKey.Name),
    56  			Form:           types.NewFormPanel(),
    57  			NewForm:        types.NewFormPanel(),
    58  			Detail:         types.NewInfoPanel(cfg.PrimaryKey.Name),
    59  			CanAdd:         cfg.CanAdd,
    60  			Editable:       cfg.Editable,
    61  			Deletable:      cfg.Deletable,
    62  			Exportable:     cfg.Exportable,
    63  			PrimaryKey:     cfg.PrimaryKey,
    64  			OnlyNewForm:    cfg.OnlyNewForm,
    65  			OnlyUpdateForm: cfg.OnlyUpdateForm,
    66  			OnlyDetail:     cfg.OnlyDetail,
    67  			OnlyInfo:       cfg.OnlyInfo,
    68  		},
    69  		connectionDriver:     cfg.Driver,
    70  		connectionDriverMode: cfg.DriverMode,
    71  		connection:           cfg.Connection,
    72  		sourceURL:            cfg.SourceURL,
    73  		getDataFun:           cfg.GetDataFun,
    74  	}
    75  }
    76  
    77  // Copy copy a new table.Table from origin DefaultTable
    78  func (tb *DefaultTable) Copy() Table {
    79  	return &DefaultTable{
    80  		BaseTable: &BaseTable{
    81  			Form: types.NewFormPanel().SetTable(tb.Form.Table).
    82  				SetDescription(tb.Form.Description).
    83  				SetTitle(tb.Form.Title),
    84  			NewForm: types.NewFormPanel().SetTable(tb.Form.Table).
    85  				SetDescription(tb.Form.Description).
    86  				SetTitle(tb.Form.Title),
    87  			Info: types.NewInfoPanel(tb.PrimaryKey.Name).SetTable(tb.Info.Table).
    88  				SetDescription(tb.Info.Description).
    89  				SetTitle(tb.Info.Title).
    90  				SetGetDataFn(tb.Info.GetDataFn),
    91  			Detail: types.NewInfoPanel(tb.PrimaryKey.Name).SetTable(tb.Detail.Table).
    92  				SetDescription(tb.Detail.Description).
    93  				SetTitle(tb.Detail.Title).
    94  				SetGetDataFn(tb.Detail.GetDataFn),
    95  			CanAdd:     tb.CanAdd,
    96  			Editable:   tb.Editable,
    97  			Deletable:  tb.Deletable,
    98  			Exportable: tb.Exportable,
    99  			PrimaryKey: tb.PrimaryKey,
   100  		},
   101  		connectionDriver:     tb.connectionDriver,
   102  		connectionDriverMode: tb.connectionDriverMode,
   103  		connection:           tb.connection,
   104  		sourceURL:            tb.sourceURL,
   105  		getDataFun:           tb.getDataFun,
   106  	}
   107  }
   108  
   109  // GetData query the data set.
   110  func (tb *DefaultTable) GetData(params parameter.Parameters) (PanelInfo, error) {
   111  
   112  	var (
   113  		data      []map[string]interface{}
   114  		size      int
   115  		beginTime = time.Now()
   116  	)
   117  
   118  	if tb.Info.UpdateParametersFns != nil {
   119  		for _, fn := range tb.Info.UpdateParametersFns {
   120  			fn(&params)
   121  		}
   122  	}
   123  
   124  	if tb.Info.QueryFilterFn != nil {
   125  		var ids []string
   126  		var stopQuery bool
   127  
   128  		if tb.getDataFun == nil && tb.Info.GetDataFn == nil {
   129  			ids, stopQuery = tb.Info.QueryFilterFn(params, tb.db())
   130  		} else {
   131  			ids, stopQuery = tb.Info.QueryFilterFn(params, nil)
   132  		}
   133  
   134  		if stopQuery {
   135  			return tb.GetDataWithIds(params.WithPKs(ids...))
   136  		}
   137  	}
   138  
   139  	if tb.getDataFun != nil {
   140  		data, size = tb.getDataFun(params)
   141  	} else if tb.sourceURL != "" {
   142  		data, size = tb.getDataFromURL(params)
   143  	} else if tb.Info.GetDataFn != nil {
   144  		data, size = tb.Info.GetDataFn(params)
   145  	} else if params.IsAll() {
   146  		return tb.getAllDataFromDatabase(params)
   147  	} else {
   148  		return tb.getDataFromDatabase(params)
   149  	}
   150  
   151  	infoList := make(types.InfoList, 0)
   152  
   153  	for i := 0; i < len(data); i++ {
   154  		infoList = append(infoList, tb.getTempModelData(data[i], params, []string{}))
   155  	}
   156  
   157  	thead, _, _, _, _, filterForm := tb.getTheadAndFilterForm(params, []string{})
   158  
   159  	endTime := time.Now()
   160  
   161  	extraInfo := ""
   162  
   163  	if !tb.Info.IsHideQueryInfo {
   164  		extraInfo = fmt.Sprintf("<b>" + language.Get("query time") + ": </b>" +
   165  			fmt.Sprintf("%.3fms", endTime.Sub(beginTime).Seconds()*1000))
   166  	}
   167  
   168  	return PanelInfo{
   169  		Thead:    thead,
   170  		InfoList: infoList,
   171  		Paginator: paginator.Get(paginator.Config{
   172  			Size:         size,
   173  			Param:        params,
   174  			PageSizeList: tb.Info.GetPageSizeList(),
   175  		}).SetExtraInfo(template.HTML(extraInfo)),
   176  		Title:          tb.Info.Title,
   177  		FilterFormData: filterForm,
   178  		Description:    tb.Info.Description,
   179  	}, nil
   180  }
   181  
   182  type GetDataFromURLRes struct {
   183  	Data []map[string]interface{}
   184  	Size int
   185  }
   186  
   187  func (tb *DefaultTable) getDataFromURL(params parameter.Parameters) ([]map[string]interface{}, int) {
   188  
   189  	u := ""
   190  	if strings.Contains(tb.sourceURL, "?") {
   191  		u = tb.sourceURL + "&" + params.Join()
   192  	} else {
   193  		u = tb.sourceURL + "?" + params.Join()
   194  	}
   195  	res, err := http.Get(u + "&pk=" + strings.Join(params.PKs(), ","))
   196  
   197  	if err != nil {
   198  		return []map[string]interface{}{}, 0
   199  	}
   200  
   201  	defer func() {
   202  		_ = res.Body.Close()
   203  	}()
   204  
   205  	body, err := ioutil.ReadAll(res.Body)
   206  
   207  	if err != nil {
   208  		return []map[string]interface{}{}, 0
   209  	}
   210  
   211  	var data GetDataFromURLRes
   212  
   213  	err = json.Unmarshal(body, &data)
   214  
   215  	if err != nil {
   216  		return []map[string]interface{}{}, 0
   217  	}
   218  
   219  	return data.Data, data.Size
   220  }
   221  
   222  // GetDataWithIds query the data set.
   223  func (tb *DefaultTable) GetDataWithIds(params parameter.Parameters) (PanelInfo, error) {
   224  
   225  	var (
   226  		data      []map[string]interface{}
   227  		size      int
   228  		beginTime = time.Now()
   229  	)
   230  
   231  	if tb.getDataFun != nil {
   232  		data, size = tb.getDataFun(params)
   233  	} else if tb.sourceURL != "" {
   234  		data, size = tb.getDataFromURL(params)
   235  	} else if tb.Info.GetDataFn != nil {
   236  		data, size = tb.Info.GetDataFn(params)
   237  	} else {
   238  		return tb.getDataFromDatabase(params)
   239  	}
   240  
   241  	infoList := make([]map[string]types.InfoItem, 0)
   242  
   243  	for i := 0; i < len(data); i++ {
   244  		infoList = append(infoList, tb.getTempModelData(data[i], params, []string{}))
   245  	}
   246  
   247  	thead, _, _, _, _, filterForm := tb.getTheadAndFilterForm(params, []string{})
   248  
   249  	endTime := time.Now()
   250  
   251  	return PanelInfo{
   252  		Thead:    thead,
   253  		InfoList: infoList,
   254  		Paginator: paginator.Get(paginator.Config{
   255  			Size:         size,
   256  			Param:        params,
   257  			PageSizeList: tb.Info.GetPageSizeList(),
   258  		}).
   259  			SetExtraInfo(template.HTML(fmt.Sprintf("<b>" + language.Get("query time") + ": </b>" +
   260  				fmt.Sprintf("%.3fms", endTime.Sub(beginTime).Seconds()*1000)))),
   261  		Title:          tb.Info.Title,
   262  		FilterFormData: filterForm,
   263  		Description:    tb.Info.Description,
   264  	}, nil
   265  }
   266  
   267  func (tb *DefaultTable) getTempModelData(res map[string]interface{}, params parameter.Parameters, columns Columns) map[string]types.InfoItem {
   268  
   269  	var tempModelData = map[string]types.InfoItem{
   270  		"__goadmin_edit_params":   {},
   271  		"__goadmin_delete_params": {},
   272  		"__goadmin_detail_params": {},
   273  	}
   274  	headField := ""
   275  	editParams := ""
   276  	deleteParams := ""
   277  	detailParams := ""
   278  
   279  	primaryKeyValue := db.GetValueFromDatabaseType(tb.PrimaryKey.Type, res[tb.PrimaryKey.Name], len(columns) == 0)
   280  
   281  	for _, field := range tb.Info.FieldList {
   282  
   283  		headField = field.Field
   284  
   285  		if field.Joins.Valid() {
   286  			headField = field.Joins.Last().GetTableName() + parameter.FilterParamJoinInfix + field.Field
   287  		}
   288  
   289  		if field.Hide {
   290  			continue
   291  		}
   292  		if !modules.InArrayWithoutEmpty(params.Columns, headField) {
   293  			continue
   294  		}
   295  
   296  		typeName := field.TypeName
   297  
   298  		if field.Joins.Valid() {
   299  			typeName = db.Varchar
   300  		}
   301  
   302  		combineValue := db.GetValueFromDatabaseType(typeName, res[headField], len(columns) == 0).String()
   303  
   304  		// TODO: ToDisplay some same logic execute repeatedly, it can be improved.
   305  		var value interface{}
   306  		if len(columns) == 0 || modules.InArray(columns, headField) || field.Joins.Valid() {
   307  			value = field.ToDisplay(types.FieldModel{
   308  				ID:    primaryKeyValue.String(),
   309  				Value: combineValue,
   310  				Row:   res,
   311  			})
   312  		} else {
   313  			value = field.ToDisplay(types.FieldModel{
   314  				ID:    primaryKeyValue.String(),
   315  				Value: "",
   316  				Row:   res,
   317  			})
   318  		}
   319  		var valueStr string
   320  		var ok bool
   321  		if valueStr, ok = value.(string); ok {
   322  			tempModelData[headField] = types.InfoItem{
   323  				Content: template.HTML(valueStr),
   324  				Value:   combineValue,
   325  			}
   326  		} else {
   327  			valueStr = string(value.(template.HTML))
   328  			tempModelData[headField] = types.InfoItem{
   329  				Content: value.(template.HTML),
   330  				Value:   combineValue,
   331  			}
   332  		}
   333  
   334  		if field.IsEditParam {
   335  			editParams += "__goadmin_edit_" + field.Field + "=" + valueStr + "&"
   336  		}
   337  		if field.IsDeleteParam {
   338  			deleteParams += "__goadmin_delete_" + field.Field + "=" + valueStr + "&"
   339  		}
   340  		if field.IsDetailParam {
   341  			detailParams += "__goadmin_detail_" + field.Field + "=" + valueStr + "&"
   342  		}
   343  	}
   344  
   345  	if editParams != "" {
   346  		tempModelData["__goadmin_edit_params"] = types.InfoItem{Content: template.HTML("&" + editParams[:len(editParams)-1])}
   347  	}
   348  	if deleteParams != "" {
   349  		tempModelData["__goadmin_delete_params"] = types.InfoItem{Content: template.HTML("&" + deleteParams[:len(deleteParams)-1])}
   350  	}
   351  	if detailParams != "" {
   352  		tempModelData["__goadmin_detail_params"] = types.InfoItem{Content: template.HTML("&" + detailParams[:len(detailParams)-1])}
   353  	}
   354  
   355  	primaryKeyField := tb.Info.FieldList.GetFieldByFieldName(tb.PrimaryKey.Name)
   356  	value := primaryKeyField.ToDisplay(types.FieldModel{
   357  		ID:    primaryKeyValue.String(),
   358  		Value: primaryKeyValue.String(),
   359  		Row:   res,
   360  	})
   361  	if valueStr, ok := value.(string); ok {
   362  		tempModelData[tb.PrimaryKey.Name] = types.InfoItem{
   363  			Content: template.HTML(valueStr),
   364  			Value:   primaryKeyValue.String(),
   365  		}
   366  	} else {
   367  		tempModelData[tb.PrimaryKey.Name] = types.InfoItem{
   368  			Content: value.(template.HTML),
   369  			Value:   primaryKeyValue.String(),
   370  		}
   371  	}
   372  
   373  	return tempModelData
   374  }
   375  
   376  func (tb *DefaultTable) getAllDataFromDatabase(params parameter.Parameters) (PanelInfo, error) {
   377  	var (
   378  		connection     = tb.db()
   379  		queryStatement = "select %s from %s %s %s %s order by " + modules.Delimiter(connection.GetDelimiter(), connection.GetDelimiter2(), "%s") + " %s"
   380  	)
   381  
   382  	columns, _ := tb.getColumns(tb.Info.Table)
   383  
   384  	thead, fields, joins := tb.Info.FieldList.GetThead(types.TableInfo{
   385  		Table:      tb.Info.Table,
   386  		Delimiter:  connection.GetDelimiter(),
   387  		Delimiter2: connection.GetDelimiter2(),
   388  		Driver:     tb.connectionDriver,
   389  		PrimaryKey: tb.PrimaryKey.Name,
   390  	}, params, columns)
   391  
   392  	fields += tb.Info.Table + "." + modules.FilterField(tb.PrimaryKey.Name, connection.GetDelimiter(), connection.GetDelimiter2())
   393  
   394  	groupBy := ""
   395  	if joins != "" {
   396  		groupBy = " GROUP BY " + tb.Info.Table + "." + modules.Delimiter(connection.GetDelimiter(), connection.GetDelimiter2(), tb.PrimaryKey.Name)
   397  	}
   398  
   399  	var (
   400  		wheres    = ""
   401  		whereArgs = make([]interface{}, 0)
   402  		existKeys = make([]string, 0)
   403  	)
   404  
   405  	wheres, whereArgs, existKeys = params.Statement(wheres, tb.Info.Table, connection.GetDelimiter(), connection.GetDelimiter2(), whereArgs, columns, existKeys,
   406  		tb.Info.FieldList.GetFieldFilterProcessValue)
   407  	wheres, whereArgs = tb.Info.Wheres.Statement(wheres, connection.GetDelimiter(), connection.GetDelimiter2(), whereArgs, existKeys, columns)
   408  	wheres, whereArgs = tb.Info.WhereRaws.Statement(wheres, whereArgs)
   409  
   410  	if wheres != "" {
   411  		wheres = " where " + wheres
   412  	}
   413  
   414  	if !modules.InArray(columns, params.SortField) {
   415  		params.SortField = tb.PrimaryKey.Name
   416  	}
   417  
   418  	queryCmd := fmt.Sprintf(queryStatement, fields, tb.Info.Table, joins, wheres, groupBy, params.SortField, params.SortType)
   419  
   420  	logger.LogSQL(queryCmd, []interface{}{})
   421  
   422  	res, err := connection.QueryWithConnection(tb.connection, queryCmd, whereArgs...)
   423  
   424  	if err != nil {
   425  		return PanelInfo{}, err
   426  	}
   427  
   428  	infoList := make([]map[string]types.InfoItem, 0)
   429  
   430  	for i := 0; i < len(res); i++ {
   431  		infoList = append(infoList, tb.getTempModelData(res[i], params, columns))
   432  	}
   433  
   434  	return PanelInfo{
   435  		InfoList:    infoList,
   436  		Thead:       thead,
   437  		Title:       tb.Info.Title,
   438  		Description: tb.Info.Description,
   439  	}, nil
   440  }
   441  
   442  // TODO: refactor
   443  func (tb *DefaultTable) getDataFromDatabase(params parameter.Parameters) (PanelInfo, error) {
   444  
   445  	var (
   446  		connection     = tb.db()
   447  		delimiter      = connection.GetDelimiter()
   448  		delimiter2     = connection.GetDelimiter2()
   449  		placeholder    = modules.Delimiter(delimiter, delimiter2, "%s")
   450  		queryStatement string
   451  		countStatement string
   452  		ids            = params.PKs()
   453  		table          = modules.Delimiter(delimiter, delimiter2, tb.Info.Table)
   454  		pk             = table + "." + modules.Delimiter(delimiter, delimiter2, tb.PrimaryKey.Name)
   455  	)
   456  
   457  	beginTime := time.Now()
   458  
   459  	if len(ids) > 0 {
   460  		countExtra := ""
   461  		if connection.Name() == db.DriverMssql {
   462  			countExtra = "as [size]"
   463  		}
   464  		// %s means: fields, table, join table, pk values, group by, order by field,  order by type
   465  		queryStatement = "select %s from " + placeholder + " %s where " + pk + " in (%s) %s ORDER BY %s." + placeholder + " %s"
   466  		// %s means: table, join table, pk values
   467  		countStatement = "select count(*) " + countExtra + " from " + placeholder + " %s where " + pk + " in (%s)"
   468  	} else {
   469  		if connection.Name() == db.DriverMssql {
   470  			// %s means: order by field, order by type, fields, table, join table, wheres, group by
   471  			queryStatement = "SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY %s." + placeholder + " %s) as ROWNUMBER_, %s from " +
   472  				placeholder + "%s %s %s ) as TMP_ WHERE TMP_.ROWNUMBER_ > ? AND TMP_.ROWNUMBER_ <= ?"
   473  			// %s means: table, join table, wheres
   474  			countStatement = "select count(*) as [size] from (select count(*) as [size] from " + placeholder + " %s %s %s) src"
   475  		} else {
   476  			// %s means: fields, table, join table, wheres, group by, order by field, order by type
   477  			queryStatement = "select %s from " + placeholder + "%s %s %s order by " + placeholder + "." + placeholder + " %s LIMIT ? OFFSET ?"
   478  			// %s means: table, join table, wheres
   479  			countStatement = "select count(*) from (select " + pk + " from " + placeholder + " %s %s %s) src"
   480  		}
   481  	}
   482  
   483  	columns, _ := tb.getColumns(tb.Info.Table)
   484  
   485  	thead, fields, joinFields, joins, joinTables, filterForm := tb.getTheadAndFilterForm(params, columns)
   486  
   487  	fields += pk
   488  
   489  	allFields := fields
   490  	groupFields := fields
   491  
   492  	if joinFields != "" {
   493  		allFields += "," + joinFields[:len(joinFields)-1]
   494  		if connection.Name() == db.DriverMssql {
   495  			for _, field := range tb.Info.FieldList {
   496  				if field.TypeName == db.Text || field.TypeName == db.Longtext {
   497  					f := modules.Delimiter(connection.GetDelimiter(), connection.GetDelimiter2(), field.Field)
   498  					headField := table + "." + f
   499  					allFields = strings.ReplaceAll(allFields, headField, "CAST("+headField+" AS NVARCHAR(MAX)) as "+f)
   500  					groupFields = strings.ReplaceAll(groupFields, headField, "CAST("+headField+" AS NVARCHAR(MAX))")
   501  				}
   502  			}
   503  		}
   504  	}
   505  
   506  	if !modules.InArray(columns, params.SortField) {
   507  		params.SortField = tb.PrimaryKey.Name
   508  	}
   509  
   510  	var (
   511  		wheres    = ""
   512  		whereArgs = make([]interface{}, 0)
   513  		args      = make([]interface{}, 0)
   514  		existKeys = make([]string, 0)
   515  	)
   516  
   517  	if len(ids) > 0 {
   518  		for _, value := range ids {
   519  			if value != "" {
   520  				wheres += "?,"
   521  				args = append(args, value)
   522  			}
   523  		}
   524  		wheres = wheres[:len(wheres)-1]
   525  	} else {
   526  
   527  		// parameter
   528  		wheres, whereArgs, existKeys = params.Statement(wheres, tb.Info.Table, connection.GetDelimiter(), connection.GetDelimiter2(), whereArgs, columns, existKeys,
   529  			tb.Info.FieldList.GetFieldFilterProcessValue)
   530  		// pre query
   531  		wheres, whereArgs = tb.Info.Wheres.Statement(wheres, connection.GetDelimiter(), connection.GetDelimiter2(), whereArgs, existKeys, columns)
   532  		wheres, whereArgs = tb.Info.WhereRaws.Statement(wheres, whereArgs)
   533  
   534  		if wheres != "" {
   535  			wheres = " where " + wheres
   536  		}
   537  
   538  		if connection.Name() == db.DriverMssql {
   539  			args = append(whereArgs, (params.PageInt-1)*params.PageSizeInt, params.PageInt*params.PageSizeInt)
   540  		} else {
   541  			args = append(whereArgs, params.PageSizeInt, (params.PageInt-1)*params.PageSizeInt)
   542  		}
   543  	}
   544  
   545  	groupBy := ""
   546  	if len(joinTables) > 0 {
   547  		if connection.Name() == db.DriverMssql {
   548  			groupBy = " GROUP BY " + groupFields
   549  		} else {
   550  			groupBy = " GROUP BY " + pk
   551  		}
   552  	}
   553  
   554  	queryCmd := ""
   555  	if connection.Name() == db.DriverMssql && len(ids) == 0 {
   556  		queryCmd = fmt.Sprintf(queryStatement, tb.Info.Table, params.SortField, params.SortType,
   557  			allFields, tb.Info.Table, joins, wheres, groupBy)
   558  	} else {
   559  		queryCmd = fmt.Sprintf(queryStatement, allFields, tb.Info.Table, joins, wheres, groupBy,
   560  			tb.Info.Table, params.SortField, params.SortType)
   561  	}
   562  
   563  	logger.LogSQL(queryCmd, args)
   564  
   565  	res, err := connection.QueryWithConnection(tb.connection, queryCmd, args...)
   566  
   567  	if err != nil {
   568  		return PanelInfo{}, err
   569  	}
   570  
   571  	infoList := make([]map[string]types.InfoItem, 0)
   572  
   573  	for i := 0; i < len(res); i++ {
   574  		infoList = append(infoList, tb.getTempModelData(res[i], params, columns))
   575  	}
   576  
   577  	// TODO: use the dialect
   578  	var size int
   579  
   580  	if len(ids) == 0 {
   581  		countCmd := fmt.Sprintf(countStatement, tb.Info.Table, joins, wheres, groupBy)
   582  
   583  		total, err := connection.QueryWithConnection(tb.connection, countCmd, whereArgs...)
   584  
   585  		if err != nil {
   586  			return PanelInfo{}, err
   587  		}
   588  
   589  		logger.LogSQL(countCmd, nil)
   590  
   591  		if tb.connectionDriver == "postgresql" {
   592  			if tb.connectionDriverMode == "h2" {
   593  				size = int(total[0]["count(*)"].(int64))
   594  			} else if config.GetDatabases().GetDefault().DriverMode == "h2" {
   595  				size = int(total[0]["count(*)"].(int64))
   596  			} else {
   597  				size = int(total[0]["count"].(int64))
   598  			}
   599  		} else if tb.connectionDriver == db.DriverMssql {
   600  			size = int(total[0]["size"].(int64))
   601  		} else {
   602  			size = int(total[0]["count(*)"].(int64))
   603  		}
   604  	}
   605  
   606  	endTime := time.Now()
   607  
   608  	return PanelInfo{
   609  		Thead:    thead,
   610  		InfoList: infoList,
   611  		Paginator: tb.GetPaginator(size, params,
   612  			template.HTML(fmt.Sprintf("<b>"+language.Get("query time")+": </b>"+
   613  				fmt.Sprintf("%.3fms", endTime.Sub(beginTime).Seconds()*1000)))),
   614  		Title:          tb.Info.Title,
   615  		FilterFormData: filterForm,
   616  		Description:    tb.Info.Description,
   617  	}, nil
   618  }
   619  
   620  func getDataRes(list []map[string]interface{}, _ int) map[string]interface{} {
   621  	if len(list) > 0 {
   622  		return list[0]
   623  	}
   624  	return nil
   625  }
   626  
   627  // GetDataWithId query the single row of data.
   628  func (tb *DefaultTable) GetDataWithId(param parameter.Parameters) (FormInfo, error) {
   629  
   630  	var (
   631  		res     map[string]interface{}
   632  		columns Columns
   633  		id      = param.PK()
   634  	)
   635  
   636  	if tb.getDataFun != nil {
   637  		res = getDataRes(tb.getDataFun(param))
   638  	} else if tb.sourceURL != "" {
   639  		res = getDataRes(tb.getDataFromURL(param))
   640  	} else if tb.Detail.GetDataFn != nil {
   641  		res = getDataRes(tb.Detail.GetDataFn(param))
   642  	} else if tb.Info.GetDataFn != nil {
   643  		res = getDataRes(tb.Info.GetDataFn(param))
   644  	} else {
   645  
   646  		columns, _ = tb.getColumns(tb.Form.Table)
   647  
   648  		var (
   649  			fields, joinFields, joins, groupBy = "", "", "", ""
   650  
   651  			err            error
   652  			joinTables     = make([]string, 0)
   653  			args           = []interface{}{id}
   654  			connection     = tb.db()
   655  			delimiter      = connection.GetDelimiter()
   656  			delimiter2     = connection.GetDelimiter2()
   657  			tableName      = modules.Delimiter(delimiter, delimiter2, tb.GetForm().Table)
   658  			pk             = tableName + "." + modules.Delimiter(delimiter, delimiter2, tb.PrimaryKey.Name)
   659  			queryStatement = "select %s from %s %s where " + pk + " = ? %s "
   660  		)
   661  
   662  		for i := 0; i < len(tb.Form.FieldList); i++ {
   663  
   664  			if tb.Form.FieldList[i].Field != pk && modules.InArray(columns, tb.Form.FieldList[i].Field) &&
   665  				!tb.Form.FieldList[i].Joins.Valid() {
   666  				fields += tableName + "." + modules.FilterField(tb.Form.FieldList[i].Field, delimiter, delimiter2) + ","
   667  			}
   668  
   669  			if tb.Form.FieldList[i].Joins.Valid() {
   670  				headField := tb.Form.FieldList[i].Joins.Last().GetTableName() + parameter.FilterParamJoinInfix + tb.Form.FieldList[i].Field
   671  				joinFields += db.GetAggregationExpression(connection.Name(), tb.Form.FieldList[i].Joins.Last().GetTableName(delimiter, delimiter2)+"."+
   672  					modules.FilterField(tb.Form.FieldList[i].Field, delimiter, delimiter2), headField, types.JoinFieldValueDelimiter) + ","
   673  				for _, join := range tb.Form.FieldList[i].Joins {
   674  					if !modules.InArray(joinTables, join.GetTableName(delimiter, delimiter2)) {
   675  						joinTables = append(joinTables, join.GetTableName(delimiter, delimiter2))
   676  						if join.BaseTable == "" {
   677  							join.BaseTable = tableName
   678  						}
   679  						joins += " left join " + modules.FilterField(join.Table, delimiter, delimiter2) + " " + join.TableAlias + " on " +
   680  							join.GetTableName(delimiter, delimiter2) + "." + modules.FilterField(join.JoinField, delimiter, delimiter2) + " = " +
   681  							join.BaseTable + "." + modules.FilterField(join.Field, delimiter, delimiter2)
   682  					}
   683  				}
   684  			}
   685  		}
   686  
   687  		fields += pk
   688  		groupFields := fields
   689  
   690  		if joinFields != "" {
   691  			fields += "," + joinFields[:len(joinFields)-1]
   692  			if connection.Name() == db.DriverMssql {
   693  				for i := 0; i < len(tb.Form.FieldList); i++ {
   694  					if tb.Form.FieldList[i].TypeName == db.Text || tb.Form.FieldList[i].TypeName == db.Longtext {
   695  						f := modules.Delimiter(connection.GetDelimiter(), connection.GetDelimiter2(), tb.Form.FieldList[i].Field)
   696  						headField := tb.Info.Table + "." + f
   697  						fields = strings.ReplaceAll(fields, headField, "CAST("+headField+" AS NVARCHAR(MAX)) as "+f)
   698  						groupFields = strings.ReplaceAll(groupFields, headField, "CAST("+headField+" AS NVARCHAR(MAX))")
   699  					}
   700  				}
   701  			}
   702  		}
   703  
   704  		if len(joinTables) > 0 {
   705  			if connection.Name() == db.DriverMssql {
   706  				groupBy = " GROUP BY " + groupFields
   707  			} else {
   708  				groupBy = " GROUP BY " + pk
   709  			}
   710  		}
   711  
   712  		queryCmd := fmt.Sprintf(queryStatement, fields, tableName, joins, groupBy)
   713  
   714  		logger.LogSQL(queryCmd, args)
   715  
   716  		result, err := connection.QueryWithConnection(tb.connection, queryCmd, args...)
   717  
   718  		if err != nil {
   719  			return FormInfo{Title: tb.Form.Title, Description: tb.Form.Description}, err
   720  		}
   721  
   722  		if len(result) == 0 {
   723  			return FormInfo{Title: tb.Form.Title, Description: tb.Form.Description}, errors.New(errs.WrongID)
   724  		}
   725  
   726  		res = result[0]
   727  	}
   728  
   729  	var (
   730  		groupFormList = make([]types.FormFields, 0)
   731  		groupHeaders  = make([]string, 0)
   732  	)
   733  
   734  	if len(tb.Form.TabGroups) > 0 {
   735  		groupFormList, groupHeaders = tb.Form.GroupFieldWithValue(tb.PrimaryKey.Name, id, columns, res, tb.sqlObjOrNil)
   736  		return FormInfo{
   737  			FieldList:         tb.Form.FieldList,
   738  			GroupFieldList:    groupFormList,
   739  			GroupFieldHeaders: groupHeaders,
   740  			Title:             tb.Form.Title,
   741  			Description:       tb.Form.Description,
   742  		}, nil
   743  	}
   744  
   745  	var fieldList = tb.Form.FieldsWithValue(tb.PrimaryKey.Name, id, columns, res, tb.sqlObjOrNil)
   746  
   747  	return FormInfo{
   748  		FieldList:         fieldList,
   749  		GroupFieldList:    groupFormList,
   750  		GroupFieldHeaders: groupHeaders,
   751  		Title:             tb.Form.Title,
   752  		Description:       tb.Form.Description,
   753  	}, nil
   754  }
   755  
   756  // UpdateData update data.
   757  func (tb *DefaultTable) UpdateData(dataList form.Values) error {
   758  
   759  	dataList.Add(form.PostTypeKey, "0")
   760  
   761  	var (
   762  		errMsg = ""
   763  		err    error
   764  	)
   765  
   766  	if tb.Form.PostHook != nil {
   767  		defer func() {
   768  			dataList.Add(form.PostTypeKey, "0")
   769  			dataList.Add(form.PostResultKey, errMsg)
   770  			go func() {
   771  				defer func() {
   772  					if err := recover(); err != nil {
   773  						logger.Error(err)
   774  					}
   775  				}()
   776  
   777  				err := tb.Form.PostHook(dataList)
   778  				if err != nil {
   779  					logger.Error(err)
   780  				}
   781  			}()
   782  		}()
   783  	}
   784  
   785  	if tb.Form.Validator != nil {
   786  		if err := tb.Form.Validator(dataList); err != nil {
   787  			errMsg = "post error: " + err.Error()
   788  			return err
   789  		}
   790  	}
   791  
   792  	if tb.Form.PreProcessFn != nil {
   793  		dataList = tb.Form.PreProcessFn(dataList)
   794  	}
   795  
   796  	if tb.Form.UpdateFn != nil {
   797  		dataList.Delete(form.PostTypeKey)
   798  		err = tb.Form.UpdateFn(tb.PreProcessValue(dataList, types.PostTypeUpdate))
   799  		if err != nil {
   800  			errMsg = "post error: " + err.Error()
   801  		}
   802  		return err
   803  	}
   804  
   805  	if len(dataList) == 0 {
   806  		return nil
   807  	}
   808  
   809  	_, err = tb.sql().Table(tb.Form.Table).
   810  		Where(tb.PrimaryKey.Name, "=", dataList.Get(tb.PrimaryKey.Name)).
   811  		Update(tb.getInjectValueFromFormValue(dataList, types.PostTypeUpdate))
   812  
   813  	// NOTE: some errors should be ignored.
   814  	if db.CheckError(err, db.UPDATE) {
   815  		if err != nil {
   816  			errMsg = "post error: " + err.Error()
   817  		}
   818  		return err
   819  	}
   820  
   821  	return nil
   822  }
   823  
   824  // InsertData insert data.
   825  func (tb *DefaultTable) InsertData(dataList form.Values) error {
   826  
   827  	dataList.Add(form.PostTypeKey, "1")
   828  
   829  	var (
   830  		id     = int64(0)
   831  		err    error
   832  		errMsg = ""
   833  		f      = tb.GetActualNewForm()
   834  	)
   835  
   836  	if f.PostHook != nil {
   837  		defer func() {
   838  			dataList.Add(form.PostTypeKey, "1")
   839  			dataList.Add(tb.GetPrimaryKey().Name, strconv.Itoa(int(id)))
   840  			dataList.Add(form.PostResultKey, errMsg)
   841  
   842  			go func() {
   843  				defer func() {
   844  					if err := recover(); err != nil {
   845  						logger.Error(err)
   846  					}
   847  				}()
   848  
   849  				err := f.PostHook(dataList)
   850  				if err != nil {
   851  					logger.Error(err)
   852  				}
   853  			}()
   854  		}()
   855  	}
   856  
   857  	if f.Validator != nil {
   858  		if err := f.Validator(dataList); err != nil {
   859  			errMsg = "post error: " + err.Error()
   860  			return err
   861  		}
   862  	}
   863  
   864  	if f.PreProcessFn != nil {
   865  		dataList = f.PreProcessFn(dataList)
   866  	}
   867  
   868  	if f.InsertFn != nil {
   869  		dataList.Delete(form.PostTypeKey)
   870  		err = f.InsertFn(tb.PreProcessValue(dataList, types.PostTypeCreate))
   871  		if err != nil {
   872  			errMsg = "post error: " + err.Error()
   873  		}
   874  		return err
   875  	}
   876  
   877  	if len(dataList) == 0 {
   878  		return nil
   879  	}
   880  
   881  	id, err = tb.sql().Table(f.Table).Insert(tb.getInjectValueFromFormValue(dataList, types.PostTypeCreate))
   882  
   883  	// NOTE: some errors should be ignored.
   884  	if db.CheckError(err, db.INSERT) {
   885  		errMsg = "post error: " + err.Error()
   886  		return err
   887  	}
   888  
   889  	return nil
   890  }
   891  
   892  func (tb *DefaultTable) getInjectValueFromFormValue(dataList form.Values, typ types.PostType) dialect.H {
   893  
   894  	var (
   895  		value         = make(dialect.H)
   896  		exceptString  = make([]string, 0)
   897  		columns, auto = tb.getColumns(tb.Form.Table)
   898  
   899  		fun types.PostFieldFilterFn
   900  	)
   901  
   902  	// If a key is a auto increment primary key, it can`t be insert or update.
   903  	if auto {
   904  		exceptString = []string{tb.PrimaryKey.Name, form.PreviousKey, form.MethodKey, form.TokenKey,
   905  			constant.IframeKey, constant.IframeIDKey}
   906  	} else {
   907  		exceptString = []string{form.PreviousKey, form.MethodKey, form.TokenKey,
   908  			constant.IframeKey, constant.IframeIDKey}
   909  	}
   910  
   911  	if !dataList.IsSingleUpdatePost() {
   912  		for i := 0; i < len(tb.Form.FieldList); i++ {
   913  			if tb.Form.FieldList[i].FormType.IsMultiSelect() {
   914  				if _, ok := dataList[tb.Form.FieldList[i].Field+"[]"]; !ok {
   915  					dataList[tb.Form.FieldList[i].Field+"[]"] = []string{""}
   916  				}
   917  			}
   918  		}
   919  	}
   920  
   921  	dataList = dataList.RemoveRemark()
   922  
   923  	for k, v := range dataList {
   924  		k = strings.ReplaceAll(k, "[]", "")
   925  		if !modules.InArray(exceptString, k) {
   926  			if modules.InArray(columns, k) {
   927  				field := tb.Form.FieldList.FindByFieldName(k)
   928  				delimiter := ","
   929  				if field != nil {
   930  					fun = field.PostFilterFn
   931  					delimiter = modules.SetDefault(field.DefaultOptionDelimiter, ",")
   932  				}
   933  				vv := modules.RemoveBlankFromArray(v)
   934  				if fun != nil {
   935  					value[k] = fun(types.PostFieldModel{
   936  						ID:       dataList.Get(tb.PrimaryKey.Name),
   937  						Value:    vv,
   938  						Row:      dataList.ToMap(),
   939  						PostType: typ,
   940  					})
   941  				} else {
   942  					if len(vv) > 1 {
   943  						value[k] = strings.Join(vv, delimiter)
   944  					} else if len(vv) > 0 {
   945  						value[k] = vv[0]
   946  					} else {
   947  						value[k] = ""
   948  					}
   949  				}
   950  			} else {
   951  				field := tb.Form.FieldList.FindByFieldName(k)
   952  				if field != nil && field.PostFilterFn != nil {
   953  					field.PostFilterFn(types.PostFieldModel{
   954  						ID:       dataList.Get(tb.PrimaryKey.Name),
   955  						Value:    modules.RemoveBlankFromArray(v),
   956  						Row:      dataList.ToMap(),
   957  						PostType: typ,
   958  					})
   959  				}
   960  			}
   961  		}
   962  	}
   963  	return value
   964  }
   965  
   966  func (tb *DefaultTable) PreProcessValue(dataList form.Values, typ types.PostType) form.Values {
   967  
   968  	exceptString := []string{form.PreviousKey, form.MethodKey, form.TokenKey,
   969  		constant.IframeKey, constant.IframeIDKey}
   970  	dataList = dataList.RemoveRemark()
   971  	var fun types.PostFieldFilterFn
   972  
   973  	for k, v := range dataList {
   974  		k = strings.ReplaceAll(k, "[]", "")
   975  		if !modules.InArray(exceptString, k) {
   976  			field := tb.Form.FieldList.FindByFieldName(k)
   977  			if field != nil {
   978  				fun = field.PostFilterFn
   979  			}
   980  			vv := modules.RemoveBlankFromArray(v)
   981  			if fun != nil {
   982  				dataList.Add(k, fmt.Sprintf("%s", fun(types.PostFieldModel{
   983  					ID:       dataList.Get(tb.PrimaryKey.Name),
   984  					Value:    vv,
   985  					Row:      dataList.ToMap(),
   986  					PostType: typ,
   987  				})))
   988  			}
   989  		}
   990  	}
   991  	return dataList
   992  }
   993  
   994  // DeleteData delete data.
   995  func (tb *DefaultTable) DeleteData(id string) error {
   996  
   997  	var (
   998  		idArr = strings.Split(id, ",")
   999  		err   error
  1000  	)
  1001  
  1002  	if tb.Info.DeleteHook != nil {
  1003  		defer func() {
  1004  			go func() {
  1005  				defer func() {
  1006  					if recoverErr := recover(); recoverErr != nil {
  1007  						logger.Error(recoverErr)
  1008  					}
  1009  				}()
  1010  
  1011  				if hookErr := tb.Info.DeleteHook(idArr); hookErr != nil {
  1012  					logger.Error(hookErr)
  1013  				}
  1014  			}()
  1015  		}()
  1016  	}
  1017  
  1018  	if tb.Info.DeleteHookWithRes != nil {
  1019  		defer func() {
  1020  			go func() {
  1021  				defer func() {
  1022  					if recoverErr := recover(); recoverErr != nil {
  1023  						logger.Error(recoverErr)
  1024  					}
  1025  				}()
  1026  
  1027  				if hookErr := tb.Info.DeleteHookWithRes(idArr, err); hookErr != nil {
  1028  					logger.Error(hookErr)
  1029  				}
  1030  			}()
  1031  		}()
  1032  	}
  1033  
  1034  	if tb.Info.PreDeleteFn != nil {
  1035  		if err = tb.Info.PreDeleteFn(idArr); err != nil {
  1036  			return err
  1037  		}
  1038  	}
  1039  
  1040  	if tb.Info.DeleteFn != nil {
  1041  		err = tb.Info.DeleteFn(idArr)
  1042  		return err
  1043  	}
  1044  
  1045  	if len(idArr) == 0 || tb.Info.Table == "" {
  1046  		err = errors.New("delete error: wrong parameter")
  1047  		return err
  1048  	}
  1049  
  1050  	err = tb.delete(tb.Info.Table, tb.PrimaryKey.Name, idArr)
  1051  	return err
  1052  }
  1053  
  1054  func (tb *DefaultTable) GetNewFormInfo() FormInfo {
  1055  
  1056  	f := tb.GetActualNewForm()
  1057  
  1058  	if len(f.TabGroups) == 0 {
  1059  		return FormInfo{FieldList: f.FieldsWithDefaultValue(tb.sqlObjOrNil)}
  1060  	}
  1061  
  1062  	newForm, headers := f.GroupField(tb.sqlObjOrNil)
  1063  
  1064  	return FormInfo{GroupFieldList: newForm, GroupFieldHeaders: headers}
  1065  }
  1066  
  1067  // ***************************************
  1068  // helper function for database operation
  1069  // ***************************************
  1070  
  1071  func (tb *DefaultTable) delete(table, key string, values []string) error {
  1072  
  1073  	var vals = make([]interface{}, len(values))
  1074  	for i, v := range values {
  1075  		vals[i] = v
  1076  	}
  1077  
  1078  	return tb.sql().Table(table).
  1079  		WhereIn(key, vals).
  1080  		Delete()
  1081  }
  1082  
  1083  func (tb *DefaultTable) getTheadAndFilterForm(params parameter.Parameters, columns Columns) (types.Thead,
  1084  	string, string, string, []string, []types.FormField) {
  1085  
  1086  	return tb.Info.FieldList.GetTheadAndFilterForm(types.TableInfo{
  1087  		Table:      tb.Info.Table,
  1088  		Delimiter:  tb.delimiter(),
  1089  		Delimiter2: tb.delimiter2(),
  1090  		Driver:     tb.connectionDriver,
  1091  		PrimaryKey: tb.PrimaryKey.Name,
  1092  	}, params, columns, tb.sqlObjOrNil)
  1093  }
  1094  
  1095  // db is a helper function return raw db connection.
  1096  func (tb *DefaultTable) db() db.Connection {
  1097  	if tb.dbObj == nil {
  1098  		tb.dbObj = db.GetConnectionFromService(services.Get(tb.connectionDriver))
  1099  	}
  1100  	return tb.dbObj
  1101  }
  1102  
  1103  func (tb *DefaultTable) delimiter() string {
  1104  	if tb.getDataFromDB() {
  1105  		return tb.db().GetDelimiter()
  1106  	}
  1107  	return ""
  1108  }
  1109  
  1110  func (tb *DefaultTable) delimiter2() string {
  1111  	if tb.getDataFromDB() {
  1112  		return tb.db().GetDelimiter2()
  1113  	}
  1114  	return ""
  1115  }
  1116  
  1117  func (tb *DefaultTable) getDataFromDB() bool {
  1118  	return tb.sourceURL == "" && tb.getDataFun == nil && tb.Info.GetDataFn == nil && tb.Detail.GetDataFn == nil
  1119  }
  1120  
  1121  // sql is a helper function return db sql.
  1122  func (tb *DefaultTable) sql() *db.SQL {
  1123  	return db.WithDriverAndConnection(tb.connection, tb.db())
  1124  }
  1125  
  1126  // sqlObjOrNil is a helper function return db sql obj or nil.
  1127  func (tb *DefaultTable) sqlObjOrNil() *db.SQL {
  1128  	if tb.connectionDriver != "" && tb.getDataFromDB() {
  1129  		return db.WithDriverAndConnection(tb.connection, tb.db())
  1130  	}
  1131  	return nil
  1132  }
  1133  
  1134  type Columns []string
  1135  
  1136  func (tb *DefaultTable) getColumns(table string) (Columns, bool) {
  1137  
  1138  	columnsModel, _ := tb.sql().Table(table).ShowColumns()
  1139  
  1140  	columns := make(Columns, len(columnsModel))
  1141  	switch tb.connectionDriver {
  1142  	case db.DriverPostgresql:
  1143  		auto := false
  1144  		for key, model := range columnsModel {
  1145  			columns[key] = model["column_name"].(string)
  1146  			if columns[key] == tb.PrimaryKey.Name {
  1147  				if v, ok := model["column_default"].(string); ok {
  1148  					if strings.Contains(v, "nextval") {
  1149  						auto = true
  1150  					}
  1151  				}
  1152  			}
  1153  		}
  1154  		return columns, auto
  1155  	case db.DriverMysql:
  1156  		auto := false
  1157  		for key, model := range columnsModel {
  1158  			columns[key] = model["Field"].(string)
  1159  			if columns[key] == tb.PrimaryKey.Name {
  1160  				if v, ok := model["Extra"].(string); ok {
  1161  					if v == "auto_increment" {
  1162  						auto = true
  1163  					}
  1164  				}
  1165  			}
  1166  		}
  1167  		return columns, auto
  1168  	case db.DriverSqlite:
  1169  		for key, model := range columnsModel {
  1170  			columns[key] = string(model["name"].(string))
  1171  		}
  1172  
  1173  		num, _ := tb.sql().Table("sqlite_sequence").
  1174  			Where("name", "=", tb.GetForm().Table).Count()
  1175  
  1176  		return columns, num > 0
  1177  	case db.DriverMssql:
  1178  		for key, model := range columnsModel {
  1179  			columns[key] = string(model["column_name"].(string))
  1180  		}
  1181  		return columns, true
  1182  	default:
  1183  		panic("wrong driver")
  1184  	}
  1185  }