| 
									
										
										
										
											2017-03-30 04:54:07 +08:00
										 |  |  | package mysql | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2021-09-07 15:35:37 +08:00
										 |  |  | 	"context" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2019-09-25 02:50:49 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2017-03-30 04:54:07 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2019-11-06 16:54:53 +08:00
										 |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2018-01-18 18:44:36 +08:00
										 |  |  | 	"reflect" | 
					
						
							| 
									
										
										
										
											2017-03-31 17:45:25 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2018-10-17 17:45:06 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2019-01-15 20:29:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-25 02:50:49 +08:00
										 |  |  | 	"github.com/VividCortex/mysqlerr" | 
					
						
							| 
									
										
										
										
											2021-10-07 22:33:50 +08:00
										 |  |  | 	"github.com/go-sql-driver/mysql" | 
					
						
							| 
									
										
										
										
											2021-09-07 15:35:37 +08:00
										 |  |  | 	"github.com/grafana/grafana-plugin-sdk-go/backend" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana-plugin-sdk-go/backend/datasource" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  | 	"github.com/grafana/grafana-plugin-sdk-go/data" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana-plugin-sdk-go/data/sqlutil" | 
					
						
							| 
									
										
										
										
											2021-05-20 05:53:41 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/httpclient" | 
					
						
							| 
									
										
										
										
											2021-10-07 22:33:50 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/log" | 
					
						
							| 
									
										
										
										
											2019-09-11 03:50:04 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/setting" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/tsdb/sqleng" | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	dateFormat      = "2006-01-02" | 
					
						
							|  |  |  | 	dateTimeFormat1 = "2006-01-02 15:04:05" | 
					
						
							|  |  |  | 	dateTimeFormat2 = "2006-01-02T15:04:05Z" | 
					
						
							| 
									
										
										
										
											2017-03-30 04:54:07 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-07 15:35:37 +08:00
										 |  |  | var logger = log.New("tsdb.mysql") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Service struct { | 
					
						
							|  |  |  | 	Cfg *setting.Cfg | 
					
						
							|  |  |  | 	im  instancemgmt.InstanceManager | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-06 16:54:53 +08:00
										 |  |  | func characterEscape(s string, escapeChar string) string { | 
					
						
							| 
									
										
										
										
											2020-09-22 22:22:19 +08:00
										 |  |  | 	return strings.ReplaceAll(s, escapeChar, url.QueryEscape(escapeChar)) | 
					
						
							| 
									
										
										
										
											2019-11-06 16:54:53 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-21 01:16:22 +08:00
										 |  |  | func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider) *Service { | 
					
						
							|  |  |  | 	return &Service{ | 
					
						
							| 
									
										
										
										
											2021-09-13 21:27:51 +08:00
										 |  |  | 		im: datasource.NewInstanceManager(newInstanceSettings(cfg, httpClientProvider)), | 
					
						
							| 
									
										
										
										
											2021-09-07 15:35:37 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-13 21:27:51 +08:00
										 |  |  | func newInstanceSettings(cfg *setting.Cfg, httpClientProvider httpclient.Provider) datasource.InstanceFactoryFunc { | 
					
						
							| 
									
										
										
										
											2021-09-07 15:35:37 +08:00
										 |  |  | 	return func(settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { | 
					
						
							|  |  |  | 		jsonData := sqleng.JsonData{ | 
					
						
							|  |  |  | 			MaxOpenConns:    0, | 
					
						
							|  |  |  | 			MaxIdleConns:    2, | 
					
						
							|  |  |  | 			ConnMaxLifetime: 14400, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		err := json.Unmarshal(settings.JSONData, &jsonData) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("error reading settings: %w", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		dsInfo := sqleng.DataSourceInfo{ | 
					
						
							|  |  |  | 			JsonData:                jsonData, | 
					
						
							|  |  |  | 			URL:                     settings.URL, | 
					
						
							|  |  |  | 			User:                    settings.User, | 
					
						
							|  |  |  | 			Database:                settings.Database, | 
					
						
							|  |  |  | 			ID:                      settings.ID, | 
					
						
							|  |  |  | 			Updated:                 settings.Updated, | 
					
						
							|  |  |  | 			UID:                     settings.UID, | 
					
						
							|  |  |  | 			DecryptedSecureJSONData: settings.DecryptedSecureJSONData, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-05-20 05:53:41 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		protocol := "tcp" | 
					
						
							| 
									
										
										
										
											2021-09-07 15:35:37 +08:00
										 |  |  | 		if strings.HasPrefix(dsInfo.URL, "/") { | 
					
						
							| 
									
										
										
										
											2021-05-20 05:53:41 +08:00
										 |  |  | 			protocol = "unix" | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-06 16:54:53 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-20 05:53:41 +08:00
										 |  |  | 		cnnstr := fmt.Sprintf("%s:%s@%s(%s)/%s?collation=utf8mb4_unicode_ci&parseTime=true&loc=UTC&allowNativePasswords=true", | 
					
						
							| 
									
										
										
										
											2021-09-07 15:35:37 +08:00
										 |  |  | 			characterEscape(dsInfo.User, ":"), | 
					
						
							|  |  |  | 			dsInfo.DecryptedSecureJSONData["password"], | 
					
						
							| 
									
										
										
										
											2021-05-20 05:53:41 +08:00
										 |  |  | 			protocol, | 
					
						
							| 
									
										
										
										
											2021-09-07 15:35:37 +08:00
										 |  |  | 			characterEscape(dsInfo.URL, ")"), | 
					
						
							|  |  |  | 			characterEscape(dsInfo.Database, "?"), | 
					
						
							| 
									
										
										
										
											2021-05-20 05:53:41 +08:00
										 |  |  | 		) | 
					
						
							| 
									
										
										
										
											2019-01-15 20:29:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-07 15:35:37 +08:00
										 |  |  | 		opts, err := settings.HTTPClientOptions() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		tlsConfig, err := httpClientProvider.GetTLSConfig(opts) | 
					
						
							| 
									
										
										
										
											2021-05-20 05:53:41 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2019-10-22 22:16:53 +08:00
										 |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-15 20:29:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-20 05:53:41 +08:00
										 |  |  | 		if tlsConfig.RootCAs != nil || len(tlsConfig.Certificates) > 0 { | 
					
						
							| 
									
										
										
										
											2021-09-07 15:35:37 +08:00
										 |  |  | 			tlsConfigString := fmt.Sprintf("ds%d", settings.ID) | 
					
						
							| 
									
										
										
										
											2021-05-20 05:53:41 +08:00
										 |  |  | 			if err := mysql.RegisterTLSConfig(tlsConfigString, tlsConfig); err != nil { | 
					
						
							|  |  |  | 				return nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			cnnstr += "&tls=" + tlsConfigString | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-03-30 19:46:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-07 15:35:37 +08:00
										 |  |  | 		if dsInfo.JsonData.Timezone != "" { | 
					
						
							|  |  |  | 			cnnstr += fmt.Sprintf("&time_zone='%s'", url.QueryEscape(dsInfo.JsonData.Timezone)) | 
					
						
							| 
									
										
										
										
											2021-05-27 16:03:03 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-13 21:27:51 +08:00
										 |  |  | 		if cfg.Env == setting.Dev { | 
					
						
							| 
									
										
										
										
											2021-05-20 05:53:41 +08:00
										 |  |  | 			logger.Debug("getEngine", "connection", cnnstr) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-03-30 04:54:07 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-20 05:53:41 +08:00
										 |  |  | 		config := sqleng.DataPluginConfiguration{ | 
					
						
							|  |  |  | 			DriverName:        "mysql", | 
					
						
							|  |  |  | 			ConnectionString:  cnnstr, | 
					
						
							| 
									
										
										
										
											2021-09-07 15:35:37 +08:00
										 |  |  | 			DSInfo:            dsInfo, | 
					
						
							| 
									
										
										
										
											2021-05-20 05:53:41 +08:00
										 |  |  | 			TimeColumnNames:   []string{"time", "time_sec"}, | 
					
						
							|  |  |  | 			MetricColumnTypes: []string{"CHAR", "VARCHAR", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT"}, | 
					
						
							| 
									
										
										
										
											2021-09-13 21:27:51 +08:00
										 |  |  | 			RowLimit:          cfg.DataProxyRowLimit, | 
					
						
							| 
									
										
										
										
											2021-05-20 05:53:41 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-21 21:07:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-20 05:53:41 +08:00
										 |  |  | 		rowTransformer := mysqlQueryResultTransformer{ | 
					
						
							|  |  |  | 			log: logger, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-07 15:35:37 +08:00
										 |  |  | 		return sqleng.NewQueryDataHandler(config, &rowTransformer, newMysqlMacroEngine(logger), logger) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *Service) getDataSourceHandler(pluginCtx backend.PluginContext) (*sqleng.DataSourceHandler, error) { | 
					
						
							|  |  |  | 	i, err := s.im.Get(pluginCtx) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	instance := i.(*sqleng.DataSourceHandler) | 
					
						
							|  |  |  | 	return instance, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { | 
					
						
							|  |  |  | 	dsHandler, err := s.getDataSourceHandler(req.PluginContext) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2021-05-20 05:53:41 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-07 15:35:37 +08:00
										 |  |  | 	return dsHandler.QueryData(ctx, req) | 
					
						
							| 
									
										
										
										
											2017-04-21 21:07:43 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-25 02:50:49 +08:00
										 |  |  | type mysqlQueryResultTransformer struct { | 
					
						
							| 
									
										
										
										
											2018-07-27 00:10:45 +08:00
										 |  |  | 	log log.Logger | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-18 18:44:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-25 02:50:49 +08:00
										 |  |  | func (t *mysqlQueryResultTransformer) TransformQueryError(err error) error { | 
					
						
							| 
									
										
										
										
											2020-11-19 21:47:17 +08:00
										 |  |  | 	var driverErr *mysql.MySQLError | 
					
						
							|  |  |  | 	if errors.As(err, &driverErr) { | 
					
						
							|  |  |  | 		if driverErr.Number != mysqlerr.ER_PARSE_ERROR && driverErr.Number != mysqlerr.ER_BAD_FIELD_ERROR && | 
					
						
							|  |  |  | 			driverErr.Number != mysqlerr.ER_NO_SUCH_TABLE { | 
					
						
							| 
									
										
										
										
											2019-09-25 02:50:49 +08:00
										 |  |  | 			t.log.Error("query error", "err", err) | 
					
						
							|  |  |  | 			return errQueryFailed | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-05 18:29:39 +08:00
										 |  |  | var errQueryFailed = errors.New("query failed - please inspect Grafana server log for details") | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func (t *mysqlQueryResultTransformer) GetConverterList() []sqlutil.StringConverter { | 
					
						
							|  |  |  | 	// For the MySQL driver , we have these possible data types:
 | 
					
						
							|  |  |  | 	// https://www.w3schools.com/sql/sql_datatypes.asp#:~:text=In%20MySQL%20there%20are%20three,numeric%2C%20and%20date%20and%20time.
 | 
					
						
							|  |  |  | 	// Since by default, we convert all into String, we need only to handle the Numeric data types
 | 
					
						
							|  |  |  | 	return []sqlutil.StringConverter{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Name:           "handle DOUBLE", | 
					
						
							|  |  |  | 			InputScanKind:  reflect.Struct, | 
					
						
							|  |  |  | 			InputTypeName:  "DOUBLE", | 
					
						
							|  |  |  | 			ConversionFunc: func(in *string) (*string, error) { return in, nil }, | 
					
						
							|  |  |  | 			Replacer: &sqlutil.StringFieldReplacer{ | 
					
						
							|  |  |  | 				OutputFieldType: data.FieldTypeNullableFloat64, | 
					
						
							|  |  |  | 				ReplaceFunc: func(in *string) (interface{}, error) { | 
					
						
							|  |  |  | 					if in == nil { | 
					
						
							|  |  |  | 						return nil, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					v, err := strconv.ParseFloat(*in, 64) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						return nil, err | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return &v, nil | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Name:           "handle BIGINT", | 
					
						
							|  |  |  | 			InputScanKind:  reflect.Struct, | 
					
						
							|  |  |  | 			InputTypeName:  "BIGINT", | 
					
						
							|  |  |  | 			ConversionFunc: func(in *string) (*string, error) { return in, nil }, | 
					
						
							|  |  |  | 			Replacer: &sqlutil.StringFieldReplacer{ | 
					
						
							|  |  |  | 				OutputFieldType: data.FieldTypeNullableInt64, | 
					
						
							|  |  |  | 				ReplaceFunc: func(in *string) (interface{}, error) { | 
					
						
							|  |  |  | 					if in == nil { | 
					
						
							|  |  |  | 						return nil, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					v, err := strconv.ParseInt(*in, 10, 64) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						return nil, err | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return &v, nil | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Name:           "handle DECIMAL", | 
					
						
							|  |  |  | 			InputScanKind:  reflect.Slice, | 
					
						
							|  |  |  | 			InputTypeName:  "DECIMAL", | 
					
						
							|  |  |  | 			ConversionFunc: func(in *string) (*string, error) { return in, nil }, | 
					
						
							|  |  |  | 			Replacer: &sqlutil.StringFieldReplacer{ | 
					
						
							|  |  |  | 				OutputFieldType: data.FieldTypeNullableFloat64, | 
					
						
							|  |  |  | 				ReplaceFunc: func(in *string) (interface{}, error) { | 
					
						
							|  |  |  | 					if in == nil { | 
					
						
							|  |  |  | 						return nil, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					v, err := strconv.ParseFloat(*in, 64) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						return nil, err | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return &v, nil | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Name:           "handle DATETIME", | 
					
						
							|  |  |  | 			InputScanKind:  reflect.Struct, | 
					
						
							|  |  |  | 			InputTypeName:  "DATETIME", | 
					
						
							|  |  |  | 			ConversionFunc: func(in *string) (*string, error) { return in, nil }, | 
					
						
							|  |  |  | 			Replacer: &sqlutil.StringFieldReplacer{ | 
					
						
							|  |  |  | 				OutputFieldType: data.FieldTypeNullableTime, | 
					
						
							|  |  |  | 				ReplaceFunc: func(in *string) (interface{}, error) { | 
					
						
							|  |  |  | 					if in == nil { | 
					
						
							|  |  |  | 						return nil, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					v, err := time.Parse(dateTimeFormat1, *in) | 
					
						
							|  |  |  | 					if err == nil { | 
					
						
							|  |  |  | 						return &v, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					v, err = time.Parse(dateTimeFormat2, *in) | 
					
						
							|  |  |  | 					if err == nil { | 
					
						
							|  |  |  | 						return &v, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					return nil, err | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Name:           "handle DATE", | 
					
						
							|  |  |  | 			InputScanKind:  reflect.Struct, | 
					
						
							|  |  |  | 			InputTypeName:  "DATE", | 
					
						
							|  |  |  | 			ConversionFunc: func(in *string) (*string, error) { return in, nil }, | 
					
						
							|  |  |  | 			Replacer: &sqlutil.StringFieldReplacer{ | 
					
						
							|  |  |  | 				OutputFieldType: data.FieldTypeNullableTime, | 
					
						
							|  |  |  | 				ReplaceFunc: func(in *string) (interface{}, error) { | 
					
						
							|  |  |  | 					if in == nil { | 
					
						
							|  |  |  | 						return nil, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					v, err := time.Parse(dateFormat, *in) | 
					
						
							|  |  |  | 					if err == nil { | 
					
						
							|  |  |  | 						return &v, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					v, err = time.Parse(dateTimeFormat1, *in) | 
					
						
							|  |  |  | 					if err == nil { | 
					
						
							|  |  |  | 						return &v, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					v, err = time.Parse(dateTimeFormat2, *in) | 
					
						
							|  |  |  | 					if err == nil { | 
					
						
							|  |  |  | 						return &v, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return nil, err | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Name:           "handle TIMESTAMP", | 
					
						
							|  |  |  | 			InputScanKind:  reflect.Struct, | 
					
						
							|  |  |  | 			InputTypeName:  "TIMESTAMP", | 
					
						
							|  |  |  | 			ConversionFunc: func(in *string) (*string, error) { return in, nil }, | 
					
						
							|  |  |  | 			Replacer: &sqlutil.StringFieldReplacer{ | 
					
						
							|  |  |  | 				OutputFieldType: data.FieldTypeNullableTime, | 
					
						
							|  |  |  | 				ReplaceFunc: func(in *string) (interface{}, error) { | 
					
						
							|  |  |  | 					if in == nil { | 
					
						
							|  |  |  | 						return nil, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					v, err := time.Parse(dateTimeFormat1, *in) | 
					
						
							|  |  |  | 					if err == nil { | 
					
						
							|  |  |  | 						return &v, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					v, err = time.Parse(dateTimeFormat2, *in) | 
					
						
							|  |  |  | 					if err == nil { | 
					
						
							|  |  |  | 						return &v, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return nil, err | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Name:           "handle YEAR", | 
					
						
							|  |  |  | 			InputScanKind:  reflect.Struct, | 
					
						
							|  |  |  | 			InputTypeName:  "YEAR", | 
					
						
							|  |  |  | 			ConversionFunc: func(in *string) (*string, error) { return in, nil }, | 
					
						
							|  |  |  | 			Replacer: &sqlutil.StringFieldReplacer{ | 
					
						
							|  |  |  | 				OutputFieldType: data.FieldTypeNullableInt64, | 
					
						
							|  |  |  | 				ReplaceFunc: func(in *string) (interface{}, error) { | 
					
						
							|  |  |  | 					if in == nil { | 
					
						
							|  |  |  | 						return nil, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					v, err := strconv.ParseInt(*in, 10, 64) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						return nil, err | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return &v, nil | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2021-06-22 00:10:45 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Name:           "handle TINYINT", | 
					
						
							|  |  |  | 			InputScanKind:  reflect.Struct, | 
					
						
							|  |  |  | 			InputTypeName:  "TINYINT", | 
					
						
							|  |  |  | 			ConversionFunc: func(in *string) (*string, error) { return in, nil }, | 
					
						
							|  |  |  | 			Replacer: &sqlutil.StringFieldReplacer{ | 
					
						
							|  |  |  | 				OutputFieldType: data.FieldTypeNullableInt64, | 
					
						
							|  |  |  | 				ReplaceFunc: func(in *string) (interface{}, error) { | 
					
						
							|  |  |  | 					if in == nil { | 
					
						
							|  |  |  | 						return nil, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					v, err := strconv.ParseInt(*in, 10, 64) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						return nil, err | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return &v, nil | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Name:           "handle SMALLINT", | 
					
						
							|  |  |  | 			InputScanKind:  reflect.Struct, | 
					
						
							|  |  |  | 			InputTypeName:  "SMALLINT", | 
					
						
							|  |  |  | 			ConversionFunc: func(in *string) (*string, error) { return in, nil }, | 
					
						
							|  |  |  | 			Replacer: &sqlutil.StringFieldReplacer{ | 
					
						
							|  |  |  | 				OutputFieldType: data.FieldTypeNullableInt64, | 
					
						
							|  |  |  | 				ReplaceFunc: func(in *string) (interface{}, error) { | 
					
						
							|  |  |  | 					if in == nil { | 
					
						
							|  |  |  | 						return nil, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					v, err := strconv.ParseInt(*in, 10, 64) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						return nil, err | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return &v, nil | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2021-05-05 22:46:07 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Name:           "handle INT", | 
					
						
							|  |  |  | 			InputScanKind:  reflect.Struct, | 
					
						
							|  |  |  | 			InputTypeName:  "INT", | 
					
						
							|  |  |  | 			ConversionFunc: func(in *string) (*string, error) { return in, nil }, | 
					
						
							|  |  |  | 			Replacer: &sqlutil.StringFieldReplacer{ | 
					
						
							|  |  |  | 				OutputFieldType: data.FieldTypeNullableInt64, | 
					
						
							|  |  |  | 				ReplaceFunc: func(in *string) (interface{}, error) { | 
					
						
							|  |  |  | 					if in == nil { | 
					
						
							|  |  |  | 						return nil, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					v, err := strconv.ParseInt(*in, 10, 64) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						return nil, err | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return &v, nil | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Name:           "handle FLOAT", | 
					
						
							|  |  |  | 			InputScanKind:  reflect.Struct, | 
					
						
							|  |  |  | 			InputTypeName:  "FLOAT", | 
					
						
							|  |  |  | 			ConversionFunc: func(in *string) (*string, error) { return in, nil }, | 
					
						
							|  |  |  | 			Replacer: &sqlutil.StringFieldReplacer{ | 
					
						
							|  |  |  | 				OutputFieldType: data.FieldTypeNullableFloat64, | 
					
						
							|  |  |  | 				ReplaceFunc: func(in *string) (interface{}, error) { | 
					
						
							|  |  |  | 					if in == nil { | 
					
						
							|  |  |  | 						return nil, nil | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					v, err := strconv.ParseFloat(*in, 64) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						return nil, err | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return &v, nil | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |