From c59dddf7afb227f756259a50fc1d2b1946a4a72f Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Tue, 27 Aug 2024 14:16:04 +0300 Subject: [PATCH] MySQL: Add parseTime=true to SQL connections (#92469) --- .../configure-grafana/feature-toggles/index.md | 1 + .../grafana-data/src/types/featureToggles.gen.ts | 1 + pkg/services/featuremgmt/registry.go | 6 ++++++ pkg/services/featuremgmt/toggles_gen.csv | 1 + pkg/services/featuremgmt/toggles_gen.go | 4 ++++ pkg/services/featuremgmt/toggles_gen.json | 12 ++++++++++++ pkg/services/searchV2/index_test.go | 6 +++++- pkg/services/sqlstore/database_config.go | 4 ++++ pkg/services/sqlstore/sqlstore.go | 14 ++++++++++++-- pkg/storage/unified/sql/db/dbimpl/dbEngine.go | 1 + 10 files changed, 47 insertions(+), 3 deletions(-) diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index 220007f2a34..cafe9c965ec 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -122,6 +122,7 @@ Experimental features might be changed or removed without prior notice. | `logRequestsInstrumentedAsUnknown` | Logs the path for requests that are instrumented as unknown | | `showDashboardValidationWarnings` | Show warnings when dashboards do not validate against the schema | | `mysqlAnsiQuotes` | Use double quotes to escape keyword in a MySQL query | +| `mysqlParseTime` | Ensure the parseTime flag is set for MySQL driver | | `alertingBacktesting` | Rule backtesting API for alerting | | `editPanelCSVDragAndDrop` | Enables drag and drop for CSV and Excel files | | `lokiQuerySplittingConfig` | Give users the option to configure split durations for Loki queries | diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 2b3667bed7a..6a7a898dcc9 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -45,6 +45,7 @@ export interface FeatureToggles { cloudWatchCrossAccountQuerying?: boolean; showDashboardValidationWarnings?: boolean; mysqlAnsiQuotes?: boolean; + mysqlParseTime?: boolean; accessControlOnCall?: boolean; nestedFolders?: boolean; alertingBacktesting?: boolean; diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index f760aa302da..653217f2158 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -214,6 +214,12 @@ var ( Stage: FeatureStageExperimental, Owner: grafanaSearchAndStorageSquad, }, + { + Name: "mysqlParseTime", + Description: "Ensure the parseTime flag is set for MySQL driver", + Stage: FeatureStageExperimental, + Owner: grafanaSearchAndStorageSquad, + }, { Name: "accessControlOnCall", Description: "Access control primitives for OnCall", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index 37eeb2bb3bc..f4b174c97d4 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -26,6 +26,7 @@ grpcServer,preview,@grafana/search-and-storage,false,false,false cloudWatchCrossAccountQuerying,GA,@grafana/aws-datasources,false,false,false showDashboardValidationWarnings,experimental,@grafana/dashboards-squad,false,false,false mysqlAnsiQuotes,experimental,@grafana/search-and-storage,false,false,false +mysqlParseTime,experimental,@grafana/search-and-storage,false,false,false accessControlOnCall,preview,@grafana/identity-access-team,false,false,false nestedFolders,GA,@grafana/search-and-storage,false,false,false alertingBacktesting,experimental,@grafana/alerting-squad,false,false,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index 29b3def626e..c8795f7466b 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -115,6 +115,10 @@ const ( // Use double quotes to escape keyword in a MySQL query FlagMysqlAnsiQuotes = "mysqlAnsiQuotes" + // FlagMysqlParseTime + // Ensure the parseTime flag is set for MySQL driver + FlagMysqlParseTime = "mysqlParseTime" + // FlagAccessControlOnCall // Access control primitives for OnCall FlagAccessControlOnCall = "accessControlOnCall" diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 533293c469b..0ae0732d919 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -1789,6 +1789,18 @@ "codeowner": "@grafana/search-and-storage" } }, + { + "metadata": { + "name": "mysqlParseTime", + "resourceVersion": "1724750152191", + "creationTimestamp": "2024-08-27T09:15:52Z" + }, + "spec": { + "description": "Ensure the parseTime flag is set for MySQL driver", + "stage": "experimental", + "codeowner": "@grafana/search-and-storage" + } + }, { "metadata": { "name": "nestedFolders", diff --git a/pkg/services/searchV2/index_test.go b/pkg/services/searchV2/index_test.go index 584253ff2ac..bc1d4b5d07b 100644 --- a/pkg/services/searchV2/index_test.go +++ b/pkg/services/searchV2/index_test.go @@ -791,7 +791,11 @@ func TestIntegrationSoftDeletion(t *testing.T) { // Set up dashboard store. quotaService := quotatest.New(false, nil) - featureToggles := featuremgmt.WithFeatures(featuremgmt.FlagPanelTitleSearch, featuremgmt.FlagDashboardRestore) + featureToggles := featuremgmt.WithFeatures( + featuremgmt.FlagPanelTitleSearch, + featuremgmt.FlagDashboardRestore, + featuremgmt.FlagMysqlParseTime, + ) dashboardStore, err := database.ProvideDashboardStore(replStore, cfg, featureToggles, tagimpl.ProvideService(sqlStore), quotaService) require.NoError(t, err) diff --git a/pkg/services/sqlstore/database_config.go b/pkg/services/sqlstore/database_config.go index e469455670f..c4af9663d13 100644 --- a/pkg/services/sqlstore/database_config.go +++ b/pkg/services/sqlstore/database_config.go @@ -166,6 +166,10 @@ func (dbCfg *DatabaseConfig) buildConnectionString(cfg *setting.Cfg, features fe cnnstr += fmt.Sprintf("&transaction_isolation=%s", val) } + if features != nil && features.IsEnabledGlobally(featuremgmt.FlagMysqlParseTime) { + cnnstr += "&parseTime=true" + } + if features != nil && features.IsEnabledGlobally(featuremgmt.FlagMysqlAnsiQuotes) { cnnstr += "&sql_mode='ANSI_QUOTES'" } diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go index e5c1a178a95..2cec8e2f975 100644 --- a/pkg/services/sqlstore/sqlstore.go +++ b/pkg/services/sqlstore/sqlstore.go @@ -296,15 +296,24 @@ func (ss *SQLStore) initEngine(engine *xorm.Engine) error { } } if engine == nil { + connection := ss.dbCfg.ConnectionString + + // Ensure that parseTime is enabled for MySQL + if ss.dbCfg.Type == migrator.MySQL && !strings.Contains(connection, "parseTime=") { + if ss.features.IsEnabledGlobally(featuremgmt.FlagMysqlParseTime) { + connection += "&parseTime=true" + } + } + var err error - engine, err = xorm.NewEngine(ss.dbCfg.Type, ss.dbCfg.ConnectionString) + engine, err = xorm.NewEngine(ss.dbCfg.Type, connection) if err != nil { return err } // Only for MySQL or MariaDB, verify we can connect with the current connection string's system var for transaction isolation. // If not, create a new engine with a compatible connection string. if ss.dbCfg.Type == migrator.MySQL { - engine, err = ss.ensureTransactionIsolationCompatibility(engine, ss.dbCfg.ConnectionString) + engine, err = ss.ensureTransactionIsolationCompatibility(engine, connection) if err != nil { return err } @@ -481,6 +490,7 @@ func getCfgForTesting(opts ...InitTestDBOpt) *setting.Cfg { func getFeaturesForTesting(opts ...InitTestDBOpt) featuremgmt.FeatureToggles { featureKeys := []any{ featuremgmt.FlagPanelTitleSearch, + featuremgmt.FlagMysqlParseTime, } for _, opt := range opts { if len(opt.FeatureFlags) > 0 { diff --git a/pkg/storage/unified/sql/db/dbimpl/dbEngine.go b/pkg/storage/unified/sql/db/dbimpl/dbEngine.go index c024c8e195a..d068a658481 100644 --- a/pkg/storage/unified/sql/db/dbimpl/dbEngine.go +++ b/pkg/storage/unified/sql/db/dbimpl/dbEngine.go @@ -31,6 +31,7 @@ func getEngineMySQL(getter *sectionGetter, tracer tracing.Tracer) (*xorm.Engine, config.Loc = time.UTC config.AllowNativePasswords = true config.ClientFoundRows = true + config.ParseTime = true // allow executing multiple SQL statements in a single roundtrip, and also // enable executing the CALL statement to run stored procedures that execute