From acc2e94c156c6822ab9fef724ec2d4a3271a4532 Mon Sep 17 00:00:00 2001 From: jennifersp Date: Sun, 7 Sep 2025 23:44:04 -0700 Subject: [PATCH 1/9] support datestyle parameter use --- go.mod | 2 +- go.sum | 4 +- server/cast/timestamp.go | 2 +- server/cast/timestamptz.go | 2 +- server/config/parameters.go | 18 +++---- server/config/parameters_list.go | 85 ++++++++++++++++++++++++++++++-- server/functions/date.go | 58 ++++++++++++++++++---- server/functions/timestamp.go | 78 ++++++++++++++++++++++++++++- server/functions/timestamptz.go | 2 +- 9 files changed, 223 insertions(+), 28 deletions(-) diff --git a/go.mod b/go.mod index 92c6b57349..c27e5fb125 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/dolthub/eventsapi_schema v0.0.0-20250725194025-a087efa1ee55 github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 github.com/dolthub/go-icu-regex v0.0.0-20250820171420-f2b78f56ce9f - github.com/dolthub/go-mysql-server v0.20.1-0.20250903174244-9aefd19469b0 + github.com/dolthub/go-mysql-server v0.20.1-0.20250908061832-4fbd4c7705c4 github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216 github.com/dolthub/vitess v0.0.0-20250902225707-0159e964d73d github.com/fatih/color v1.13.0 diff --git a/go.sum b/go.sum index c37e754edd..04d4b2eba4 100644 --- a/go.sum +++ b/go.sum @@ -258,8 +258,8 @@ github.com/dolthub/fslock v0.0.3 h1:iLMpUIvJKMKm92+N1fmHVdxJP5NdyDK5bK7z7Ba2s2U= github.com/dolthub/fslock v0.0.3/go.mod h1:QWql+P17oAAMLnL4HGB5tiovtDuAjdDTPbuqx7bYfa0= github.com/dolthub/go-icu-regex v0.0.0-20250820171420-f2b78f56ce9f h1:oSA8CptGeCEdTdD9LFtv8x4juDfdaLKsx1eocyaj1bE= github.com/dolthub/go-icu-regex v0.0.0-20250820171420-f2b78f56ce9f/go.mod h1:kpsRG+a196Y69zsAFL0RkQICII9a571lcaxhvQnmrdY= -github.com/dolthub/go-mysql-server v0.20.1-0.20250903174244-9aefd19469b0 h1:+Vzm00h8mKgfSGeMiGE6cSSosgh0EF/OZCUS7StzyW0= -github.com/dolthub/go-mysql-server v0.20.1-0.20250903174244-9aefd19469b0/go.mod h1:ymoHIRZoZKO1EH9iUGcq4E6XyIpUaMgZz3ZPeWa828w= +github.com/dolthub/go-mysql-server v0.20.1-0.20250908061832-4fbd4c7705c4 h1:s3lJxaDw/s30r+/Ng7zPKgh/64Py5KmGe5TRkDz2Fbo= +github.com/dolthub/go-mysql-server v0.20.1-0.20250908061832-4fbd4c7705c4/go.mod h1:ymoHIRZoZKO1EH9iUGcq4E6XyIpUaMgZz3ZPeWa828w= github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63 h1:OAsXLAPL4du6tfbBgK0xXHZkOlos63RdKYS3Sgw/dfI= github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63/go.mod h1:lV7lUeuDhH5thVGDCKXbatwKy2KW80L4rMT46n+Y2/Q= github.com/dolthub/ishell v0.0.0-20240701202509-2b217167d718 h1:lT7hE5k+0nkBdj/1UOSFwjWpNxf+LCApbRHgnCA17XE= diff --git a/server/cast/timestamp.go b/server/cast/timestamp.go index 838455e605..13fa22f340 100644 --- a/server/cast/timestamp.go +++ b/server/cast/timestamp.go @@ -44,7 +44,7 @@ func timestampAssignment() { FromType: pgtypes.Timestamp, ToType: pgtypes.Time, Function: func(ctx *sql.Context, val any, targetType *pgtypes.DoltgresType) (any, error) { - return timeofday.FromTime(val.(time.Time)), nil + return timeofday.FromTime(val.(time.Time)).ToTime(), nil }, }) } diff --git a/server/cast/timestamptz.go b/server/cast/timestamptz.go index bf97754996..68b43ed249 100644 --- a/server/cast/timestamptz.go +++ b/server/cast/timestamptz.go @@ -44,7 +44,7 @@ func timestampTZAssignment() { FromType: pgtypes.TimestampTZ, ToType: pgtypes.Time, Function: func(ctx *sql.Context, val any, targetType *pgtypes.DoltgresType) (any, error) { - return timeofday.FromTime(val.(time.Time)), nil + return timeofday.FromTime(val.(time.Time)).ToTime(), nil }, }) framework.MustAddAssignmentTypeCast(framework.TypeCast{ diff --git a/server/config/parameters.go b/server/config/parameters.go index f31d273334..85959729d9 100644 --- a/server/config/parameters.go +++ b/server/config/parameters.go @@ -70,7 +70,7 @@ type Parameter struct { Source ParameterSource ResetVal any Scope sql.SystemVariableScope - ValidateFunc func(any) (any, bool) + ValidateFunc func(currVal, newVal any) (any, bool) } // GetName implements sql.SystemVariable. @@ -99,32 +99,32 @@ func (p *Parameter) GetDefault() any { } // InitValue implements sql.SystemVariable. -func (p *Parameter) InitValue(ctx *sql.Context, val any, global bool) (sql.SystemVarValue, error) { - convertedVal, _, err := p.Type.Convert(ctx, val) +func (p *Parameter) InitValue(ctx *sql.Context, currVal, newVal any, global bool) (sql.SystemVarValue, error) { + convertedNewVal, _, err := p.Type.Convert(ctx, newVal) if err != nil { return sql.SystemVarValue{}, err } if p.ValidateFunc != nil { - v, ok := p.ValidateFunc(convertedVal) + v, ok := p.ValidateFunc(currVal, convertedNewVal) if !ok { - return sql.SystemVarValue{}, ErrInvalidValue.New(p.Name, convertedVal) + return sql.SystemVarValue{}, ErrInvalidValue.New(p.Name, convertedNewVal) } - convertedVal = v + convertedNewVal = v } svv := sql.SystemVarValue{ Var: p, - Val: convertedVal, + Val: convertedNewVal, } return svv, nil } // SetValue implements sql.SystemVariable. -func (p *Parameter) SetValue(ctx *sql.Context, val any, global bool) (sql.SystemVarValue, error) { +func (p *Parameter) SetValue(ctx *sql.Context, currVal, newVal any, global bool) (sql.SystemVarValue, error) { if p.IsReadOnly() { return sql.SystemVarValue{}, ErrCannotChangeAtRuntime.New(p.Name) } // TODO: Do parsing of units for memory and time parameters - return p.InitValue(ctx, val, global) + return p.InitValue(ctx, currVal, newVal, global) } // IsReadOnly implements sql.SystemVariable. diff --git a/server/config/parameters_list.go b/server/config/parameters_list.go index d2df9aa9fd..23c796f1a8 100644 --- a/server/config/parameters_list.go +++ b/server/config/parameters_list.go @@ -15,6 +15,7 @@ package config import ( + "fmt" "math" "strings" "time" @@ -699,6 +700,84 @@ var postgresConfigParameters = map[string]sql.SystemVariable{ ResetVal: "ISO, MDY", // Sourcefile: postgresql.conf Scope: GetPgsqlScope(PsqlScopeSession), + ValidateFunc: func(curr, new any) (any, bool) { + ds := "ISO" + do := "MDY" + if curr == nil { + return fmt.Sprintf(`%s, %s`, ds, do), true + } else if oldVal, ok := curr.(string); !ok { + return "", false + } else { + // the first must be date style and second must be date ordering + currentVal := strings.Split(strings.ReplaceAll(oldVal, " ", ""), ",") + if len(currentVal) != 2 { + return "", false + } + ds = currentVal[0] + do = currentVal[1] + } + newVal, ok := new.(string) + if !ok { + return "", false + } + + var dsSet, doSet bool + values := strings.Split(strings.ReplaceAll(strings.ToLower(newVal), " ", ""), ",") + for _, value := range values { + switch value { + case "iso": + if dsSet { + return "", false + } + ds = "ISO" + dsSet = true + case "sql": + if dsSet { + return "", false + } + ds = "SQL" + dsSet = true + case "postgres": + if dsSet { + return "", false + } + ds = "Postgres" + dsSet = true + case "german": + if dsSet { + return "", false + } + ds = "German" + dsSet = true + if !doSet { + do = "DMY" + } + case "us", "noneuropean", "mdy": + // MDY + if doSet { + return "", false + } + do = "MDY" + doSet = true + case "european", "dmy": + // DMY + if doSet { + return "", false + } + do = "DMY" + doSet = true + case "ymd": + if doSet { + return "", false + } + do = "YMD" + doSet = true + default: + return "", false + } + } + return fmt.Sprintf(`%s, %s`, ds, do), true + }, }, "db_user_namespace": &Parameter{ Name: "db_user_namespace", @@ -3464,8 +3543,8 @@ var postgresConfigParameters = map[string]sql.SystemVariable{ // BootVal: "GMT", ResetVal: "America/Los_Angeles", Scope: GetPgsqlScope(PsqlScopeSession), - ValidateFunc: func(a any) (any, bool) { - switch v := a.(type) { + ValidateFunc: func(_, new any) (any, bool) { + switch v := new.(type) { case string: if strings.ToLower(v) == "local" { // TODO: fix this @@ -3486,7 +3565,7 @@ var postgresConfigParameters = map[string]sql.SystemVariable{ return nil, false } } - return a, true + return new, true }, }, "timezone_abbreviations": &Parameter{ diff --git a/server/functions/date.go b/server/functions/date.go index 5e37da4635..93883cd810 100644 --- a/server/functions/date.go +++ b/server/functions/date.go @@ -15,6 +15,7 @@ package functions import ( + "strings" "time" "github.com/dolthub/go-mysql-server/sql" @@ -41,15 +42,16 @@ var date_in = framework.Function1{ Strict: true, Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) { input := val.(string) - if date, _, err := pgdate.ParseDate(time.Now(), pgdate.ParseModeYMD, input); err == nil { - return date.ToTime() - } else if date, _, err = pgdate.ParseDate(time.Now(), pgdate.ParseModeDMY, input); err == nil { - return date.ToTime() - } else if date, _, err = pgdate.ParseDate(time.Now(), pgdate.ParseModeMDY, input); err == nil { - return date.ToTime() - } else { - return nil, err + formatsInOrder := getDateStyleInputFormat(ctx) + var date pgdate.Date + var err error + for _, format := range formatsInOrder { + date, _, err = pgdate.ParseDate(time.Now(), format, input) + if err == nil { + return date.ToTime() + } } + return nil, err }, } @@ -60,7 +62,7 @@ var date_out = framework.Function1{ Parameters: [1]*pgtypes.DoltgresType{pgtypes.Date}, Strict: true, Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) { - return FormatDateTimeWithBC(val.(time.Time), "2006-01-02", false), nil + return FormatDateTimeWithBC(val.(time.Time), getLayoutStringFormat(ctx, true), false), nil }, } @@ -106,3 +108,41 @@ var date_cmp = framework.Function2{ return int32(ab.Compare(bb)), nil }, } + +// getDateStyleInputFormat returns set the defined format in DateStyle config as the first in the ordered list of date parsing modes. +// TODO: this or something similar should be used in postgres/parser/sem/tree/datum.go when parsing timestamp/timestamptz/date values. +func getDateStyleInputFormat(ctx *sql.Context) []pgdate.ParseMode { + formatsInOrder := []pgdate.ParseMode{pgdate.ParseModeYMD, pgdate.ParseModeDMY, pgdate.ParseModeDMY} // default + if ctx == nil { + return formatsInOrder + } + val, err := ctx.GetSessionVariable(ctx, "datestyle") + if err != nil { + return formatsInOrder + } + + ds := strings.ReplaceAll(val.(string), " ", "") + values := strings.Split(ds, ",") + setFormat := pgdate.ParseModeYMD + for _, value := range values { + switch value { + case "MDY": + setFormat = pgdate.ParseModeMDY + case "DMY": + setFormat = pgdate.ParseModeDMY + case "YMD": + setFormat = pgdate.ParseModeYMD + } + } + if setFormat == formatsInOrder[0] { + return formatsInOrder + } + + curFirst := formatsInOrder[0] + for i, f := range formatsInOrder { + if setFormat == f { + formatsInOrder[i] = curFirst + } + } + return formatsInOrder +} diff --git a/server/functions/timestamp.go b/server/functions/timestamp.go index d7cf98db4d..bc64215e4a 100644 --- a/server/functions/timestamp.go +++ b/server/functions/timestamp.go @@ -16,6 +16,7 @@ package functions import ( "fmt" + "strings" "time" "github.com/dolthub/go-mysql-server/sql" @@ -64,7 +65,7 @@ var timestamp_out = framework.Function1{ Strict: true, Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) { t := val.(time.Time) - return FormatDateTimeWithBC(t, "2006-01-02 15:04:05.999999", false), nil + return FormatDateTimeWithBC(t, getLayoutStringFormat(ctx, false), false), nil }, } @@ -140,6 +141,81 @@ var timestamp_cmp = framework.Function2{ }, } +// https://www.postgresql.org/docs/15/datatype-datetime.html +const ( + DateStyleISO = "ISO" + DateStyleSQL = "SQL" + DateStylePostgres = "Postgres" + DateStyleGerman = "German" +) + +func getDateStyleOutputFormat(ctx *sql.Context) string { + format := DateStyleISO // default + if ctx == nil { + return format + } + + val, err := ctx.GetSessionVariable(ctx, "datestyle") + if err != nil { + return format + } + + values := strings.Split(strings.ReplaceAll(val.(string), " ", ""), ",") + for _, value := range values { + switch value { + case DateStyleISO, DateStyleSQL, DateStylePostgres, DateStyleGerman: + return value + } + } + return format +} + +const ( + // ISO is ISO 8601, SQL standard + dateStyleFormat_ISO = "2006-01-02 15:04:05.999999" + dateStyleFormatDateOnly_ISO = "2006-01-02" + // SQL is traditional style + dateStyleFormat_SQL = "01/02/2006 15:04:05.999999" + dateStyleFormatDateOnly_SQL = "01/02/2006" + // Postgres is original style + dateStyleFormat_Postgres = "Mon Jan 02 15:04:05.999999 2006" + dateStyleFormatDateOnly_Postgres = "Mon Jan 02 2006" + // regional style + dateStyleFormat_German = "02.01.2006 15:04:05.999999" + dateStyleFormatDateOnly_German = "02.01.2006" +) + +func getLayoutStringFormat(ctx *sql.Context, dateOnly bool) string { + layout := getDateStyleOutputFormat(ctx) + switch layout { + case DateStyleISO: + if dateOnly { + return dateStyleFormatDateOnly_ISO + } + return dateStyleFormat_ISO + case DateStyleSQL: + if dateOnly { + return dateStyleFormatDateOnly_SQL + } + return dateStyleFormat_SQL + case DateStylePostgres: + if dateOnly { + return dateStyleFormatDateOnly_Postgres + } + return dateStyleFormat_Postgres + case DateStyleGerman: + if dateOnly { + return dateStyleFormatDateOnly_German + } + return dateStyleFormat_German + } + // shouldn't happen but return default + if dateOnly { + return dateStyleFormatDateOnly_ISO + } + return dateStyleFormat_ISO +} + // FormatDateTimeWithBC formats a time.Time that may represent BC dates (negative years) // PostgreSQL represents BC years as negative years in time.Time, but Go's Format() doesn't handle this correctly // tz is optional timezone value to be appended to formatted value diff --git a/server/functions/timestamptz.go b/server/functions/timestamptz.go index 31e1ac799c..418ad30a4d 100644 --- a/server/functions/timestamptz.go +++ b/server/functions/timestamptz.go @@ -73,7 +73,7 @@ var timestamptz_out = framework.Function1{ t := val.(time.Time).In(serverLoc) // Format timestamp with BC support and timezone - return FormatDateTimeWithBC(t, "2006-01-02 15:04:05.999999", true), nil + return FormatDateTimeWithBC(t, getLayoutStringFormat(ctx, false), true), nil }, } From 96078e7d1e087df90a7c5412c34dca7119ddd7cc Mon Sep 17 00:00:00 2001 From: jennifersp Date: Mon, 8 Sep 2025 15:09:19 -0700 Subject: [PATCH 2/9] add tests for datestyle --- server/functions/date.go | 3 +- testing/go/parameters_test.go | 74 ++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/server/functions/date.go b/server/functions/date.go index 93883cd810..3017cf6c66 100644 --- a/server/functions/date.go +++ b/server/functions/date.go @@ -112,7 +112,7 @@ var date_cmp = framework.Function2{ // getDateStyleInputFormat returns set the defined format in DateStyle config as the first in the ordered list of date parsing modes. // TODO: this or something similar should be used in postgres/parser/sem/tree/datum.go when parsing timestamp/timestamptz/date values. func getDateStyleInputFormat(ctx *sql.Context) []pgdate.ParseMode { - formatsInOrder := []pgdate.ParseMode{pgdate.ParseModeYMD, pgdate.ParseModeDMY, pgdate.ParseModeDMY} // default + formatsInOrder := []pgdate.ParseMode{pgdate.ParseModeMDY, pgdate.ParseModeDMY, pgdate.ParseModeYMD} // default if ctx == nil { return formatsInOrder } @@ -144,5 +144,6 @@ func getDateStyleInputFormat(ctx *sql.Context) []pgdate.ParseMode { formatsInOrder[i] = curFirst } } + formatsInOrder[0] = setFormat return formatsInOrder } diff --git a/testing/go/parameters_test.go b/testing/go/parameters_test.go index 75997bcc63..fef3091ce0 100644 --- a/testing/go/parameters_test.go +++ b/testing/go/parameters_test.go @@ -23,7 +23,7 @@ import ( func TestParameters(t *testing.T) { RunScripts(t, []ScriptTest{ { - Name: "parameters", + Name: "default_with_oids", Assertions: []ScriptTestAssertion{ { Query: "SELECT default_with_oids;", @@ -35,5 +35,77 @@ func TestParameters(t *testing.T) { }, }, }, + { + Name: "DateStyle", + Assertions: []ScriptTestAssertion{ + { + Query: "SHOW DateStyle;", + Expected: []sql.Row{{"ISO, MDY"}}, + }, + { + Query: "SELECT timestamp '2001/02/04 04:05:06.789';", + Expected: []sql.Row{{"2001-02-04 04:05:06.789"}}, + }, + { + Query: "SET datestyle = 'german';", + Expected: []sql.Row{}, + }, + { + Query: "SHOW DateStyle;", + Expected: []sql.Row{{"German, DMY"}}, + }, + { + Skip: true, // TODO: the test passes but pgx cannot parse the result + Query: "SELECT timestamp '2001/02/04 04:05:06.789';", + Expected: []sql.Row{{"04.02.2001 04:05:06.789"}}, + }, + { + Query: "SET datestyle = 'YMD';", + Expected: []sql.Row{}, + }, + { + Query: "SHOW DateStyle;", + Expected: []sql.Row{{"German, YMD"}}, + }, + { + Query: "SET datestyle = 'sQl';", + Expected: []sql.Row{}, + }, + { + Query: "SHOW DateStyle;", + Expected: []sql.Row{{"SQL, YMD"}}, + }, + { + Skip: true, // TODO: the test passes but pgx cannot parse the result + Query: "SELECT timestamp '2001/02/04 04:05:06.789';", + Expected: []sql.Row{{"02/04/2001 04:05:06.789"}}, + }, + { + Query: "SET datestyle = 'postgreS';", + Expected: []sql.Row{}, + }, + { + Query: "SHOW DateStyle;", + Expected: []sql.Row{{"Postgres, YMD"}}, + }, + { + Skip: true, // TODO: the test passes but pgx cannot parse the result + Query: "SELECT timestamp '2001/02/04 04:05:06.789';", + Expected: []sql.Row{{"Sun Feb 04 04:05:06.789 2001"}}, + }, + { + Query: "RESET datestyle;", + Expected: []sql.Row{}, + }, + { + Query: "SHOW DateStyle;", + Expected: []sql.Row{{"ISO, MDY"}}, + }, + { + Query: "SET datestyle = 'unknown';", + ExpectedErr: `invalid value for parameter "DateStyle": "unknown"`, + }, + }, + }, }) } From 25729195c924942ae36c5afae3c26a8e4de40ceb Mon Sep 17 00:00:00 2001 From: jennifersp Date: Wed, 10 Sep 2025 22:05:38 -0700 Subject: [PATCH 3/9] set initial datestyle --- go.mod | 2 +- go.sum | 4 ++-- server/connection_handler.go | 19 +++++++++++++++---- server/doltgres_handler.go | 5 +++++ 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 8973404f55..900d3c3d65 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/dolthub/eventsapi_schema v0.0.0-20250725194025-a087efa1ee55 github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 github.com/dolthub/go-icu-regex v0.0.0-20250820171420-f2b78f56ce9f - github.com/dolthub/go-mysql-server v0.20.1-0.20250910230026-248b1e8cb615 + github.com/dolthub/go-mysql-server v0.20.1-0.20250911044832-24e6b035f03a github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216 github.com/dolthub/vitess v0.0.0-20250902225707-0159e964d73d github.com/fatih/color v1.13.0 diff --git a/go.sum b/go.sum index 664c9eb1b7..d106aabe6c 100644 --- a/go.sum +++ b/go.sum @@ -258,8 +258,8 @@ github.com/dolthub/fslock v0.0.3 h1:iLMpUIvJKMKm92+N1fmHVdxJP5NdyDK5bK7z7Ba2s2U= github.com/dolthub/fslock v0.0.3/go.mod h1:QWql+P17oAAMLnL4HGB5tiovtDuAjdDTPbuqx7bYfa0= github.com/dolthub/go-icu-regex v0.0.0-20250820171420-f2b78f56ce9f h1:oSA8CptGeCEdTdD9LFtv8x4juDfdaLKsx1eocyaj1bE= github.com/dolthub/go-icu-regex v0.0.0-20250820171420-f2b78f56ce9f/go.mod h1:kpsRG+a196Y69zsAFL0RkQICII9a571lcaxhvQnmrdY= -github.com/dolthub/go-mysql-server v0.20.1-0.20250910230026-248b1e8cb615 h1:j9lSm+NWy+SmpQdopwSDUy5gKOTBhOOZ7BxtTqgd4qk= -github.com/dolthub/go-mysql-server v0.20.1-0.20250910230026-248b1e8cb615/go.mod h1:ymoHIRZoZKO1EH9iUGcq4E6XyIpUaMgZz3ZPeWa828w= +github.com/dolthub/go-mysql-server v0.20.1-0.20250911044832-24e6b035f03a h1:PnnU03RfTPFAlz4vhveAFKpbGprd4xK+9ddpyuK1Ihg= +github.com/dolthub/go-mysql-server v0.20.1-0.20250911044832-24e6b035f03a/go.mod h1:ymoHIRZoZKO1EH9iUGcq4E6XyIpUaMgZz3ZPeWa828w= github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63 h1:OAsXLAPL4du6tfbBgK0xXHZkOlos63RdKYS3Sgw/dfI= github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63/go.mod h1:lV7lUeuDhH5thVGDCKXbatwKy2KW80L4rMT46n+Y2/Q= github.com/dolthub/ishell v0.0.0-20240701202509-2b217167d718 h1:lT7hE5k+0nkBdj/1UOSFwjWpNxf+LCApbRHgnCA17XE= diff --git a/server/connection_handler.go b/server/connection_handler.go index a4fd6169e6..39ada647c2 100644 --- a/server/connection_handler.go +++ b/server/connection_handler.go @@ -225,7 +225,7 @@ func (h *ConnectionHandler) handleStartup() (bool, error) { if err = h.sendClientStartupMessages(); err != nil { return false, err } - if err = h.chooseInitialDatabase(sm); err != nil { + if err = h.chooseInitialParameters(sm); err != nil { return false, err } return true, h.send(&pgproto3.ReadyForQuery{ @@ -282,9 +282,20 @@ func (h *ConnectionHandler) sendClientStartupMessages() error { }) } -// chooseInitialDatabase attempts to choose the initial database for the connection, -// if one is specified in the startup message provided -func (h *ConnectionHandler) chooseInitialDatabase(startupMessage *pgproto3.StartupMessage) error { +// chooseInitialParameters attempts to choose the initial parameter settings for the connection, +// if one is specified in the startup message provided. +func (h *ConnectionHandler) chooseInitialParameters(startupMessage *pgproto3.StartupMessage) error { + for name, value := range startupMessage.Parameters { + // TODO: handle other parameters defined in StartupMessage + switch strings.ToLower(name) { + case "datestyle": + err := h.doltgresHandler.InitSessionParameter(context.Background(), h.mysqlConn, "DateStyle", value) + if err != nil { + return err + } + } + } + // set initial database db, ok := startupMessage.Parameters["database"] dbSpecified := ok && len(db) > 0 if !dbSpecified { diff --git a/server/doltgres_handler.go b/server/doltgres_handler.go index 213a22157c..cdfb16d472 100644 --- a/server/doltgres_handler.go +++ b/server/doltgres_handler.go @@ -259,6 +259,11 @@ func (h *DoltgresHandler) NewContext(ctx context.Context, c *mysql.Conn, query s return h.sm.NewContextWithQuery(ctx, c, query) } +// InitSessionParameter sets a value to specified parameter for given session. +func (h *DoltgresHandler) InitSessionParameter(ctx context.Context, c *mysql.Conn, name, value string) error { + return h.sm.InitSessionVariable(ctx, c, name, value) +} + // convertBindParameters handles the conversion from bind parameters to variable values. func (h *DoltgresHandler) convertBindParameters(ctx *sql.Context, types []uint32, formatCodes []int16, values [][]byte) (map[string]sqlparser.Expr, error) { bindings := make(map[string]sqlparser.Expr, len(values)) From 93e0eb7d26e1e604bc32dbc9a5415d4086154a1a Mon Sep 17 00:00:00 2001 From: jennifersp Date: Thu, 11 Sep 2025 13:38:40 -0700 Subject: [PATCH 4/9] update gms and fix setDefault method --- go.mod | 2 +- go.sum | 4 ++-- server/config/parameters.go | 4 +++- server/config/parameters_list.go | 15 +++++---------- server/connection_handler.go | 2 +- server/doltgres_handler.go | 6 +++--- 6 files changed, 15 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 900d3c3d65..f8afc3df15 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/dolthub/eventsapi_schema v0.0.0-20250725194025-a087efa1ee55 github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 github.com/dolthub/go-icu-regex v0.0.0-20250820171420-f2b78f56ce9f - github.com/dolthub/go-mysql-server v0.20.1-0.20250911044832-24e6b035f03a + github.com/dolthub/go-mysql-server v0.20.1-0.20250911203031-9319872a4220 github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216 github.com/dolthub/vitess v0.0.0-20250902225707-0159e964d73d github.com/fatih/color v1.13.0 diff --git a/go.sum b/go.sum index d106aabe6c..88b29de2cc 100644 --- a/go.sum +++ b/go.sum @@ -258,8 +258,8 @@ github.com/dolthub/fslock v0.0.3 h1:iLMpUIvJKMKm92+N1fmHVdxJP5NdyDK5bK7z7Ba2s2U= github.com/dolthub/fslock v0.0.3/go.mod h1:QWql+P17oAAMLnL4HGB5tiovtDuAjdDTPbuqx7bYfa0= github.com/dolthub/go-icu-regex v0.0.0-20250820171420-f2b78f56ce9f h1:oSA8CptGeCEdTdD9LFtv8x4juDfdaLKsx1eocyaj1bE= github.com/dolthub/go-icu-regex v0.0.0-20250820171420-f2b78f56ce9f/go.mod h1:kpsRG+a196Y69zsAFL0RkQICII9a571lcaxhvQnmrdY= -github.com/dolthub/go-mysql-server v0.20.1-0.20250911044832-24e6b035f03a h1:PnnU03RfTPFAlz4vhveAFKpbGprd4xK+9ddpyuK1Ihg= -github.com/dolthub/go-mysql-server v0.20.1-0.20250911044832-24e6b035f03a/go.mod h1:ymoHIRZoZKO1EH9iUGcq4E6XyIpUaMgZz3ZPeWa828w= +github.com/dolthub/go-mysql-server v0.20.1-0.20250911203031-9319872a4220 h1:0oGeylTL1BHvchWzPMRFeYfLMyT6Qz/+MZcuKLtCJYs= +github.com/dolthub/go-mysql-server v0.20.1-0.20250911203031-9319872a4220/go.mod h1:ymoHIRZoZKO1EH9iUGcq4E6XyIpUaMgZz3ZPeWa828w= github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63 h1:OAsXLAPL4du6tfbBgK0xXHZkOlos63RdKYS3Sgw/dfI= github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63/go.mod h1:lV7lUeuDhH5thVGDCKXbatwKy2KW80L4rMT46n+Y2/Q= github.com/dolthub/ishell v0.0.0-20240701202509-2b217167d718 h1:lT7hE5k+0nkBdj/1UOSFwjWpNxf+LCApbRHgnCA17XE= diff --git a/server/config/parameters.go b/server/config/parameters.go index 85959729d9..840bd369da 100644 --- a/server/config/parameters.go +++ b/server/config/parameters.go @@ -90,7 +90,9 @@ func (p *Parameter) GetSessionScope() sql.SystemVariableScope { // SetDefault implements sql.SystemVariable. func (p *Parameter) SetDefault(a any) { - p.Default = a + if validatedVal, ok := p.ValidateFunc(p.Default, a); ok { + p.Default = validatedVal + } } // GetDefault implements sql.SystemVariable. diff --git a/server/config/parameters_list.go b/server/config/parameters_list.go index 23c796f1a8..e2b47b1fa3 100644 --- a/server/config/parameters_list.go +++ b/server/config/parameters_list.go @@ -703,18 +703,13 @@ var postgresConfigParameters = map[string]sql.SystemVariable{ ValidateFunc: func(curr, new any) (any, bool) { ds := "ISO" do := "MDY" - if curr == nil { - return fmt.Sprintf(`%s, %s`, ds, do), true - } else if oldVal, ok := curr.(string); !ok { - return "", false - } else { - // the first must be date style and second must be date ordering + if oldVal, ok := curr.(string); ok { currentVal := strings.Split(strings.ReplaceAll(oldVal, " ", ""), ",") - if len(currentVal) != 2 { - return "", false + if len(currentVal) == 2 { + // the first must be date style and second must be date ordering + ds = currentVal[0] + do = currentVal[1] } - ds = currentVal[0] - do = currentVal[1] } newVal, ok := new.(string) if !ok { diff --git a/server/connection_handler.go b/server/connection_handler.go index 39ada647c2..10bd2a239d 100644 --- a/server/connection_handler.go +++ b/server/connection_handler.go @@ -289,7 +289,7 @@ func (h *ConnectionHandler) chooseInitialParameters(startupMessage *pgproto3.Sta // TODO: handle other parameters defined in StartupMessage switch strings.ToLower(name) { case "datestyle": - err := h.doltgresHandler.InitSessionParameter(context.Background(), h.mysqlConn, "DateStyle", value) + err := h.doltgresHandler.InitSessionParameterDefault(context.Background(), h.mysqlConn, "DateStyle", value) if err != nil { return err } diff --git a/server/doltgres_handler.go b/server/doltgres_handler.go index cdfb16d472..6b15c839c2 100644 --- a/server/doltgres_handler.go +++ b/server/doltgres_handler.go @@ -259,9 +259,9 @@ func (h *DoltgresHandler) NewContext(ctx context.Context, c *mysql.Conn, query s return h.sm.NewContextWithQuery(ctx, c, query) } -// InitSessionParameter sets a value to specified parameter for given session. -func (h *DoltgresHandler) InitSessionParameter(ctx context.Context, c *mysql.Conn, name, value string) error { - return h.sm.InitSessionVariable(ctx, c, name, value) +// InitSessionParameterDefault sets a default value to specified parameter for a session. +func (h *DoltgresHandler) InitSessionParameterDefault(ctx context.Context, c *mysql.Conn, name, value string) error { + return h.sm.InitSessionDefaultVariable(ctx, c, name, value) } // convertBindParameters handles the conversion from bind parameters to variable values. From d6eb9bd3fa7c0dcde0170b68a53e436d42fae5f7 Mon Sep 17 00:00:00 2001 From: jennifersp Date: Thu, 11 Sep 2025 15:27:25 -0700 Subject: [PATCH 5/9] fix day of week value for negative time value when formatted --- server/functions/timestamp.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/server/functions/timestamp.go b/server/functions/timestamp.go index bc64215e4a..e861ac4fb0 100644 --- a/server/functions/timestamp.go +++ b/server/functions/timestamp.go @@ -240,6 +240,15 @@ func FormatDateTimeWithBC(t time.Time, layout string, hasTZ bool) string { // Create a new time with the positive year for formatting positiveTime := time.Date(absYear, t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location()) + // we convert the negative year to positive year to get formatted result, but it creates issue on + // the day of week value, so we get day of the week value from original negative time value. + if strings.HasPrefix(layout, "Mon") { + layout = strings.TrimPrefix(layout, "Mon") + formatted := positiveTime.Format(layout) + dayOfWeek := t.Format("Mon") + return fmt.Sprintf("%s%s BC", dayOfWeek, formatted) + } + // Format with the positive year, then append " BC" formatted := positiveTime.Format(layout) return fmt.Sprintf("%s BC", formatted) From 91f818c1f978a185876882d596fad502e702ffb6 Mon Sep 17 00:00:00 2001 From: jennifersp Date: Thu, 11 Sep 2025 17:01:05 -0700 Subject: [PATCH 6/9] fix timezone layout for postgres datestyle --- server/functions/timestamp.go | 50 +++++++++++++++++++++-------------- server/functions/timezone.go | 3 +-- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/server/functions/timestamp.go b/server/functions/timestamp.go index e861ac4fb0..898021e094 100644 --- a/server/functions/timestamp.go +++ b/server/functions/timestamp.go @@ -223,15 +223,7 @@ func FormatDateTimeWithBC(t time.Time, layout string, hasTZ bool) string { year := t.Year() isBC := year <= 0 - if hasTZ { - _, offset := t.Zone() - if offset%3600 != 0 { - layout = layout + "-07:00" - } else { - layout = layout + "-07" - } - } - + var formattedTime string if isBC { // Convert from PostgreSQL's BC representation to positive year for formatting // PostgreSQL: year 0 = 1 BC, year -1 = 2 BC, etc. @@ -240,20 +232,38 @@ func FormatDateTimeWithBC(t time.Time, layout string, hasTZ bool) string { // Create a new time with the positive year for formatting positiveTime := time.Date(absYear, t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location()) - // we convert the negative year to positive year to get formatted result, but it creates issue on - // the day of week value, so we get day of the week value from original negative time value. + // Convert the negative year to positive year to get formatted result, but it creates issue on + // the day of week value, so we need to get day of the week value from original negative time value. if strings.HasPrefix(layout, "Mon") { - layout = strings.TrimPrefix(layout, "Mon") - formatted := positiveTime.Format(layout) - dayOfWeek := t.Format("Mon") - return fmt.Sprintf("%s%s BC", dayOfWeek, formatted) + formattedTime = fmt.Sprintf("%s%s", t.Format("Mon"), positiveTime.Format(strings.TrimPrefix(layout, "Mon"))) + } else { + // Format with the positive year, then append " BC" + formattedTime = positiveTime.Format(layout) } - - // Format with the positive year, then append " BC" - formatted := positiveTime.Format(layout) - return fmt.Sprintf("%s BC", formatted) } else { // For AD years (positive), use normal formatting - return t.Format(layout) + formattedTime = t.Format(layout) + } + + if hasTZ { + name, offset := t.Zone() + if strings.HasPrefix(layout, "Mon") { + // Postgres doesn't show timezone for ones that don't have timezone abbreviation. + if name != "" { + name = t.Format("MST") + } + formattedTime += fmt.Sprintf(" %s", name) + } else { + if offset%3600 != 0 { + formattedTime += t.Format("-07:00") + } else { + formattedTime += t.Format("-07") + } + } + } + + if isBC { + return formattedTime + " BC" } + return formattedTime } diff --git a/server/functions/timezone.go b/server/functions/timezone.go index 52097d418b..80a1d31665 100644 --- a/server/functions/timezone.go +++ b/server/functions/timezone.go @@ -15,7 +15,6 @@ package functions import ( - "fmt" "strings" "time" @@ -184,5 +183,5 @@ func convertTzToOffsetSecs(t time.Time, tz string) (*time.Location, int32, bool, } _, offsetSecsUnconverted := t.Zone() - return time.FixedZone(fmt.Sprintf("fixed offset:%d", offsetSecsUnconverted), -offsetSecsUnconverted), int32(-offsetSecsUnconverted), true, nil + return time.FixedZone("", -offsetSecsUnconverted), int32(-offsetSecsUnconverted), true, nil } From 0658e60a7f2e0068949ad8b9672b39bb13bd410f Mon Sep 17 00:00:00 2001 From: jennifersp Date: Fri, 12 Sep 2025 09:40:33 -0700 Subject: [PATCH 7/9] fix test --- testing/go/functions_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/go/functions_test.go b/testing/go/functions_test.go index ab3b70a76c..cfb49919d4 100644 --- a/testing/go/functions_test.go +++ b/testing/go/functions_test.go @@ -3507,7 +3507,7 @@ func TestFormatFunctions(t *testing.T) { { Query: `SELECT to_char('2012-12-12 12:00 -02:00'::timestamptz, 'TZ');`, Expected: []sql.Row{ - {" "}, + {""}, }, }, { From 74e800076821c7158d73c0705737126eaa26a009 Mon Sep 17 00:00:00 2001 From: jennifersp Date: Tue, 16 Sep 2025 12:55:25 -0700 Subject: [PATCH 8/9] update for feedback --- server/functions/date.go | 2 +- server/functions/timestamp.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/server/functions/date.go b/server/functions/date.go index 3017cf6c66..b5cb752d1b 100644 --- a/server/functions/date.go +++ b/server/functions/date.go @@ -109,7 +109,7 @@ var date_cmp = framework.Function2{ }, } -// getDateStyleInputFormat returns set the defined format in DateStyle config as the first in the ordered list of date parsing modes. +// getDateStyleInputFormat sets the defined format in DateStyle config as the first in the ordered list of date parsing modes. // TODO: this or something similar should be used in postgres/parser/sem/tree/datum.go when parsing timestamp/timestamptz/date values. func getDateStyleInputFormat(ctx *sql.Context) []pgdate.ParseMode { formatsInOrder := []pgdate.ParseMode{pgdate.ParseModeMDY, pgdate.ParseModeDMY, pgdate.ParseModeYMD} // default diff --git a/server/functions/timestamp.go b/server/functions/timestamp.go index 898021e094..bde4163f82 100644 --- a/server/functions/timestamp.go +++ b/server/functions/timestamp.go @@ -149,6 +149,7 @@ const ( DateStyleGerman = "German" ) +// getDateStyleOutputFormat returns the format layout for date/time values defined in the 'DateStyle' configuration parameter. func getDateStyleOutputFormat(ctx *sql.Context) string { format := DateStyleISO // default if ctx == nil { From 1dc3e8dc7992f4e07b57297f93839e2cc144e593 Mon Sep 17 00:00:00 2001 From: jennifersp Date: Tue, 16 Sep 2025 14:56:24 -0700 Subject: [PATCH 9/9] update gms --- go.mod | 2 +- go.sum | 4 ++-- server/config/parameters.go | 20 ++++++++++++-------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index d9d40d50b2..9d462e449e 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/dolthub/dolt/go v0.40.5-0.20250916084833-f121f4db6fdd github.com/dolthub/eventsapi_schema v0.0.0-20250915094920-eadfd39051ca github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 - github.com/dolthub/go-mysql-server v0.20.1-0.20250916073142-59b34ad42ad7 + github.com/dolthub/go-mysql-server v0.20.1-0.20250916214234-32c0ab73f498 github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216 github.com/dolthub/vitess v0.0.0-20250915221346-753c44800850 github.com/fatih/color v1.13.0 diff --git a/go.sum b/go.sum index 2fcc552183..9a56d7c231 100644 --- a/go.sum +++ b/go.sum @@ -258,8 +258,8 @@ github.com/dolthub/fslock v0.0.3 h1:iLMpUIvJKMKm92+N1fmHVdxJP5NdyDK5bK7z7Ba2s2U= github.com/dolthub/fslock v0.0.3/go.mod h1:QWql+P17oAAMLnL4HGB5tiovtDuAjdDTPbuqx7bYfa0= github.com/dolthub/go-icu-regex v0.0.0-20250916051405-78a38d478790 h1:zxMsH7RLiG+dlZ/y0LgJHTV26XoiSJcuWq+em6t6VVc= github.com/dolthub/go-icu-regex v0.0.0-20250916051405-78a38d478790/go.mod h1:F3cnm+vMRK1HaU6+rNqQrOCyR03HHhR1GWG2gnPOqaE= -github.com/dolthub/go-mysql-server v0.20.1-0.20250916073142-59b34ad42ad7 h1:VgmY6t806cVmFRZ5YcE5awJ8YW2opk1roQhjm5Bx260= -github.com/dolthub/go-mysql-server v0.20.1-0.20250916073142-59b34ad42ad7/go.mod h1:fkgI5RU7ABC5gknW+5Q6LUMv12Y2zlkXuDQX7n7su88= +github.com/dolthub/go-mysql-server v0.20.1-0.20250916214234-32c0ab73f498 h1:XaYtlmLwSa6PwWzKQBvXi8TVIEaOsRyryAq17tT48fQ= +github.com/dolthub/go-mysql-server v0.20.1-0.20250916214234-32c0ab73f498/go.mod h1:fkgI5RU7ABC5gknW+5Q6LUMv12Y2zlkXuDQX7n7su88= github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63 h1:OAsXLAPL4du6tfbBgK0xXHZkOlos63RdKYS3Sgw/dfI= github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63/go.mod h1:lV7lUeuDhH5thVGDCKXbatwKy2KW80L4rMT46n+Y2/Q= github.com/dolthub/ishell v0.0.0-20240701202509-2b217167d718 h1:lT7hE5k+0nkBdj/1UOSFwjWpNxf+LCApbRHgnCA17XE= diff --git a/server/config/parameters.go b/server/config/parameters.go index 840bd369da..a0d1bff595 100644 --- a/server/config/parameters.go +++ b/server/config/parameters.go @@ -101,32 +101,36 @@ func (p *Parameter) GetDefault() any { } // InitValue implements sql.SystemVariable. -func (p *Parameter) InitValue(ctx *sql.Context, currVal, newVal any, global bool) (sql.SystemVarValue, error) { - convertedNewVal, _, err := p.Type.Convert(ctx, newVal) +func (p *Parameter) InitValue(ctx *sql.Context, val any, global bool) (sql.SystemVarValue, error) { + convertedVal, _, err := p.Type.Convert(ctx, val) + if err != nil { + return sql.SystemVarValue{}, err + } + currVal, err := ctx.GetSessionVariable(ctx, p.Name) if err != nil { return sql.SystemVarValue{}, err } if p.ValidateFunc != nil { - v, ok := p.ValidateFunc(currVal, convertedNewVal) + v, ok := p.ValidateFunc(currVal, convertedVal) if !ok { - return sql.SystemVarValue{}, ErrInvalidValue.New(p.Name, convertedNewVal) + return sql.SystemVarValue{}, ErrInvalidValue.New(p.Name, convertedVal) } - convertedNewVal = v + convertedVal = v } svv := sql.SystemVarValue{ Var: p, - Val: convertedNewVal, + Val: convertedVal, } return svv, nil } // SetValue implements sql.SystemVariable. -func (p *Parameter) SetValue(ctx *sql.Context, currVal, newVal any, global bool) (sql.SystemVarValue, error) { +func (p *Parameter) SetValue(ctx *sql.Context, val any, global bool) (sql.SystemVarValue, error) { if p.IsReadOnly() { return sql.SystemVarValue{}, ErrCannotChangeAtRuntime.New(p.Name) } // TODO: Do parsing of units for memory and time parameters - return p.InitValue(ctx, currVal, newVal, global) + return p.InitValue(ctx, val, global) } // IsReadOnly implements sql.SystemVariable.