diff --git a/go/mysql/sql_error.go b/go/mysql/sql_error.go index e3c8ff6594b..fcf02abaf13 100644 --- a/go/mysql/sql_error.go +++ b/go/mysql/sql_error.go @@ -162,6 +162,7 @@ var stateToMysqlCode = map[vterrors.State]struct { vterrors.DbCreateExists: {num: ERDbCreateExists, state: SSUnknownSQLState}, vterrors.DbDropExists: {num: ERDbDropExists, state: SSUnknownSQLState}, vterrors.EmptyQuery: {num: EREmptyQuery, state: SSClientError}, + vterrors.IncorrectGlobalLocalVar: {num: ERIncorrectGlobalLocalVar, state: SSUnknownSQLState}, vterrors.InnodbReadOnly: {num: ERInnodbReadOnly, state: SSUnknownSQLState}, vterrors.LockOrActiveTransaction: {num: ERLockOrActiveTransaction, state: SSUnknownSQLState}, vterrors.NoDB: {num: ERNoDb, state: SSNoDB}, diff --git a/go/test/endtoend/vtgate/reservedconn/sysvar_test.go b/go/test/endtoend/vtgate/reservedconn/sysvar_test.go index 3201fdd7521..69531781085 100644 --- a/go/test/endtoend/vtgate/reservedconn/sysvar_test.go +++ b/go/test/endtoend/vtgate/reservedconn/sysvar_test.go @@ -22,6 +22,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" @@ -373,3 +374,24 @@ func TestSystemVariableType(t *testing.T) { checkedExec(t, conn, "set autocommit = true") assertResponseMatch(t, conn, query1, query2) } + +func TestSysvarSocket(t *testing.T) { + vtParams := mysql.ConnParams{ + Host: "localhost", + Port: clusterInstance.VtgateMySQLPort, + } + conn, err := mysql.Connect(context.Background(), &vtParams) + require.NoError(t, err) + defer conn.Close() + + qr := checkedExec(t, conn, "select @@socket") + assert.Contains(t, fmt.Sprintf("%v", qr.Rows), "mysql.sock") + + _, err = exec(t, conn, "set socket = '/any/path'") + require.Error(t, err) + sqlErr, ok := err.(*mysql.SQLError) + require.True(t, ok, "not a mysql error: %T", err) + assert.Equal(t, mysql.ERIncorrectGlobalLocalVar, sqlErr.Number()) + assert.Equal(t, mysql.SSUnknownSQLState, sqlErr.SQLState()) + assert.Equal(t, "Variable 'socket' is a read only variable (errno 1238) (sqlstate HY000) during query: set socket = '/any/path'", sqlErr.Error()) +} diff --git a/go/vt/sqlparser/ast_rewriting.go b/go/vt/sqlparser/ast_rewriting.go index 6b075f731ab..08838508031 100644 --- a/go/vt/sqlparser/ast_rewriting.go +++ b/go/vt/sqlparser/ast_rewriting.go @@ -246,18 +246,19 @@ func (er *expressionRewriter) sysVarRewrite(cursor *Cursor, node *ColName) { switch lowered { case sysvars.Autocommit.Name, sysvars.ClientFoundRows.Name, - sysvars.SkipQueryPlanCache.Name, - sysvars.SQLSelectLimit.Name, - sysvars.TransactionMode.Name, - sysvars.Workload.Name, sysvars.DDLStrategy.Name, - sysvars.SessionUUID.Name, - sysvars.SessionEnableSystemSettings.Name, + sysvars.TransactionMode.Name, sysvars.ReadAfterWriteGTID.Name, sysvars.ReadAfterWriteTimeOut.Name, + sysvars.SessionEnableSystemSettings.Name, + sysvars.SessionTrackGTIDs.Name, + sysvars.SessionUUID.Name, + sysvars.SkipQueryPlanCache.Name, + sysvars.Socket.Name, + sysvars.SQLSelectLimit.Name, sysvars.Version.Name, sysvars.VersionComment.Name, - sysvars.SessionTrackGTIDs.Name: + sysvars.Workload.Name: cursor.Replace(bindVarExpression("__vt" + lowered)) er.bindVars.AddSysVar(lowered) } diff --git a/go/vt/sqlparser/ast_rewriting_test.go b/go/vt/sqlparser/ast_rewriting_test.go index 721e7ccdb44..c2368d81514 100644 --- a/go/vt/sqlparser/ast_rewriting_test.go +++ b/go/vt/sqlparser/ast_rewriting_test.go @@ -31,7 +31,7 @@ type myTestCase struct { liid, db, foundRows, rowCount, rawGTID, rawTimeout, sessTrackGTID bool ddlStrategy, sessionUUID, sessionEnableSystemSettings bool udv int - autocommit, clientFoundRows, skipQueryPlanCache bool + autocommit, clientFoundRows, skipQueryPlanCache, socket bool sqlSelectLimit, transactionMode, workload, version, versionComment bool } @@ -146,6 +146,10 @@ func TestRewrites(in *testing.T) { in: "SELECT @@workload", expected: "SELECT :__vtworkload as `@@workload`", workload: true, + }, { + in: "SELECT @@socket", + expected: "SELECT :__vtsocket as `@@socket`", + socket: true, }, { in: "select (select 42) from dual", expected: "select 42 as `(select 42 from dual)` from dual", @@ -198,6 +202,7 @@ func TestRewrites(in *testing.T) { rawGTID: true, rawTimeout: true, sessTrackGTID: true, + socket: true, }, { in: "SHOW GLOBAL VARIABLES", expected: "SHOW GLOBAL VARIABLES", @@ -215,6 +220,7 @@ func TestRewrites(in *testing.T) { rawGTID: true, rawTimeout: true, sessTrackGTID: true, + socket: true, }} for _, tc := range tests { @@ -251,6 +257,7 @@ func TestRewrites(in *testing.T) { assert.Equal(tc.sessTrackGTID, result.NeedsSysVar(sysvars.SessionTrackGTIDs.Name), "should need sessTrackGTID") assert.Equal(tc.version, result.NeedsSysVar(sysvars.Version.Name), "should need Vitess version") assert.Equal(tc.versionComment, result.NeedsSysVar(sysvars.VersionComment.Name), "should need Vitess version") + assert.Equal(tc.socket, result.NeedsSysVar(sysvars.Socket.Name), "should need :__vtsocket") }) } } diff --git a/go/vt/sysvars/sysvars.go b/go/vt/sysvars/sysvars.go index 40cb7818cf3..13d3b5159cc 100644 --- a/go/vt/sysvars/sysvars.go +++ b/go/vt/sysvars/sysvars.go @@ -44,17 +44,19 @@ var ( utf8 = "'utf8'" Autocommit = SystemVariable{Name: "autocommit", IsBoolean: true, Default: on} + Charset = SystemVariable{Name: "charset", Default: utf8, IdentifierAsString: true} ClientFoundRows = SystemVariable{Name: "client_found_rows", IsBoolean: true, Default: off} + SessionEnableSystemSettings = SystemVariable{Name: "enable_system_settings", IsBoolean: true, Default: on} + Names = SystemVariable{Name: "names", Default: utf8, IdentifierAsString: true} + SessionUUID = SystemVariable{Name: "session_uuid", IdentifierAsString: true} SkipQueryPlanCache = SystemVariable{Name: "skip_query_plan_cache", IsBoolean: true, Default: off} - TxReadOnly = SystemVariable{Name: "tx_read_only", IsBoolean: true, Default: off} - TransactionReadOnly = SystemVariable{Name: "transaction_read_only", IsBoolean: true, Default: off} + Socket = SystemVariable{Name: "socket", Default: off} SQLSelectLimit = SystemVariable{Name: "sql_select_limit", Default: off} TransactionMode = SystemVariable{Name: "transaction_mode", IdentifierAsString: true} + TransactionReadOnly = SystemVariable{Name: "transaction_read_only", IsBoolean: true, Default: off} + TxReadOnly = SystemVariable{Name: "tx_read_only", IsBoolean: true, Default: off} Workload = SystemVariable{Name: "workload", IdentifierAsString: true} - Charset = SystemVariable{Name: "charset", Default: utf8, IdentifierAsString: true} - Names = SystemVariable{Name: "names", Default: utf8, IdentifierAsString: true} - SessionUUID = SystemVariable{Name: "session_uuid", IdentifierAsString: true} - SessionEnableSystemSettings = SystemVariable{Name: "enable_system_settings", IsBoolean: true, Default: on} + // Online DDL DDLStrategy = SystemVariable{Name: "ddl_strategy", IdentifierAsString: true} Version = SystemVariable{Name: "version"} @@ -84,6 +86,12 @@ var ( SessionTrackGTIDs, } + ReadOnly = []SystemVariable{ + Socket, + Version, + VersionComment, + } + IgnoreThese = []SystemVariable{ {Name: "big_tables", IsBoolean: true}, {Name: "bulk_insert_buffer_size"}, @@ -249,5 +257,6 @@ func GetInterestingVariables() []string { // Also add version and version comment res = append(res, Version.Name) res = append(res, VersionComment.Name) + res = append(res, Socket.Name) return res } diff --git a/go/vt/vterrors/state.go b/go/vt/vterrors/state.go index 7be707e3508..b3103cd851d 100644 --- a/go/vt/vterrors/state.go +++ b/go/vt/vterrors/state.go @@ -29,6 +29,7 @@ const ( DataOutOfRange EmptyQuery ForbidSchemaChange + IncorrectGlobalLocalVar NonUniqTable SyntaxError WrongGroupField diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index e61995aa6e3..b7fc158fb47 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -322,6 +322,8 @@ func (e *Executor) addNeededBindVars(bindVarNeeds *sqlparser.BindVarNeeds, bindV bindVars[key] = sqltypes.StringBindVariable(servenv.AppVersion.MySQLVersion()) case sysvars.VersionComment.Name: bindVars[key] = sqltypes.StringBindVariable(servenv.AppVersion.String()) + case sysvars.Socket.Name: + bindVars[key] = sqltypes.StringBindVariable(mysqlSocketPath()) } } diff --git a/go/vt/vtgate/executor_select_test.go b/go/vt/vtgate/executor_select_test.go index 928abd01532..09bf6ee8ff2 100644 --- a/go/vt/vtgate/executor_select_test.go +++ b/go/vt/vtgate/executor_select_test.go @@ -318,7 +318,7 @@ func TestSelectSystemVariables(t *testing.T) { sql := "select @@autocommit, @@client_found_rows, @@skip_query_plan_cache, @@enable_system_settings, " + "@@sql_select_limit, @@transaction_mode, @@workload, @@read_after_write_gtid, " + - "@@read_after_write_timeout, @@session_track_gtids, @@ddl_strategy" + "@@read_after_write_timeout, @@session_track_gtids, @@ddl_strategy, @@socket" result, err := executorExec(executor, sql, map[string]*querypb.BindVariable{}) wantResult := &sqltypes.Result{ @@ -334,6 +334,7 @@ func TestSelectSystemVariables(t *testing.T) { {Name: "@@read_after_write_timeout", Type: sqltypes.Float64}, {Name: "@@session_track_gtids", Type: sqltypes.VarBinary}, {Name: "@@ddl_strategy", Type: sqltypes.VarBinary}, + {Name: "@@socket", Type: sqltypes.VarBinary}, }, Rows: [][]sqltypes.Value{{ // the following are the uninitialised session values @@ -349,6 +350,7 @@ func TestSelectSystemVariables(t *testing.T) { sqltypes.NewFloat64(13), sqltypes.NewVarBinary("own_gtid"), sqltypes.NewVarBinary(""), + sqltypes.NewVarBinary(""), }}, } require.NoError(t, err) diff --git a/go/vt/vtgate/executor_set_test.go b/go/vt/vtgate/executor_set_test.go index 5f18d34eb1b..aa177d77cde 100644 --- a/go/vt/vtgate/executor_set_test.go +++ b/go/vt/vtgate/executor_set_test.go @@ -246,6 +246,9 @@ func TestExecutorSet(t *testing.T) { }, { in: "set @@enable_system_settings = false", out: &vtgatepb.Session{Autocommit: true, EnableSystemSettings: false}, + }, { + in: "set @@socket = '/tmp/change.sock'", + err: "Variable 'socket' is a read only variable", }} for i, tcase := range testcases { t.Run(fmt.Sprintf("%d-%s", i, tcase.in), func(t *testing.T) { diff --git a/go/vt/vtgate/planbuilder/set.go b/go/vt/vtgate/planbuilder/set.go index 267199eae59..d34e9397abb 100644 --- a/go/vt/vtgate/planbuilder/set.go +++ b/go/vt/vtgate/planbuilder/set.go @@ -102,6 +102,12 @@ func buildSetPlan(stmt *sqlparser.Set, vschema ContextVSchema) (engine.Primitive }, nil } +func buildSetOpReadOnly(s setting) planFunc { + return func(expr *sqlparser.SetExpr, schema ContextVSchema, _ *expressionConverter) (engine.SetOp, error) { + return nil, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.IncorrectGlobalLocalVar, "Variable '%s' is a read only variable", expr.Name) + } +} + func buildNotSupported(setting) planFunc { return func(expr *sqlparser.SetExpr, schema ContextVSchema, _ *expressionConverter) (engine.SetOp, error) { return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%s: system setting is not supported", expr.Name) diff --git a/go/vt/vtgate/planbuilder/system_variables.go b/go/vt/vtgate/planbuilder/system_variables.go index ce02cb497ff..d6e9cbeaa14 100644 --- a/go/vt/vtgate/planbuilder/system_variables.go +++ b/go/vt/vtgate/planbuilder/system_variables.go @@ -27,6 +27,7 @@ import ( ) func init() { + forSettings(sysvars.ReadOnly, buildSetOpReadOnly) forSettings(sysvars.IgnoreThese, buildSetOpIgnore) forSettings(sysvars.UseReservedConn, buildSetOpReservedConn) forSettings(sysvars.CheckAndIgnore, buildSetOpCheckAndIgnore) diff --git a/go/vt/vtgate/planbuilder/testdata/set_cases.txt b/go/vt/vtgate/planbuilder/testdata/set_cases.txt index 1a00e120319..fd4b95a5ba8 100644 --- a/go/vt/vtgate/planbuilder/testdata/set_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/set_cases.txt @@ -522,3 +522,8 @@ Gen4 plan same as above } } Gen4 plan same as above + +# change read only variable +"set socket = ''" +"Variable 'socket' is a read only variable" +Gen4 plan same as above diff --git a/go/vt/vtgate/plugin_mysql_server.go b/go/vt/vtgate/plugin_mysql_server.go index 212f3ba4261..42fcd9b1266 100644 --- a/go/vt/vtgate/plugin_mysql_server.go +++ b/go/vt/vtgate/plugin_mysql_server.go @@ -540,6 +540,13 @@ func rollbackAtShutdown() { log.Errorf("All connections did not go idle. Shutting down anyway.") } +func mysqlSocketPath() string { + if mysqlServerSocketPath == nil { + return "" + } + return *mysqlServerSocketPath +} + func init() { servenv.OnRun(initMySQLProtocol) servenv.OnTermSync(shutdownMysqlProtocolAndDrain)