Skip to content

implicit commit on ddl statements #2818

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jan 22, 2025
13 changes: 10 additions & 3 deletions engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ func (e *Engine) QueryWithBindings(ctx *sql.Context, query string, parsed sqlpar
}

var schema sql.Schema
iter, schema = rowexec.FinalizeIters(ctx, analyzed, qFlags, iter)
iter, schema, err = rowexec.FinalizeIters(ctx, analyzed, qFlags, iter)
if err != nil {
clearAutocommitErr := clearAutocommitTransaction(ctx)
if clearAutocommitErr != nil {
Expand Down Expand Up @@ -493,7 +493,7 @@ func (e *Engine) PrepQueryPlanForExecution(ctx *sql.Context, _ string, plan sql.
}

var schema sql.Schema
iter, schema = rowexec.FinalizeIters(ctx, plan, qFlags, iter)
iter, schema, err = rowexec.FinalizeIters(ctx, plan, qFlags, iter)
if err != nil {
clearAutocommitErr := clearAutocommitTransaction(ctx)
if clearAutocommitErr != nil {
Expand Down Expand Up @@ -855,7 +855,14 @@ func (e *Engine) executeEvent(ctx *sql.Context, dbName, createEventStatement, us
return err
}

iter, _ = rowexec.FinalizeIters(ctx, definitionNode, nil, iter)
iter, _, err = rowexec.FinalizeIters(ctx, definitionNode, nil, iter)
if err != nil {
clearAutocommitErr := clearAutocommitTransaction(ctx)
if clearAutocommitErr != nil {
return clearAutocommitErr
}
return err
}

// Drain the iterate to execute the event body/definition
// NOTE: No row data is returned for an event; we just need to execute the statements
Expand Down
2 changes: 1 addition & 1 deletion engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ func TestTrackProcess(t *testing.T) {

iter, err := rowexec.DefaultBuilder.Build(ctx, result, nil)
require.NoError(err)
iter, _ = rowexec.FinalizeIters(ctx, result, nil, iter)
iter, _, err = rowexec.FinalizeIters(ctx, result, nil, iter)
require.NoError(err)
_, err = sql.RowIterToRows(ctx, iter)
require.NoError(err)
Expand Down
262 changes: 262 additions & 0 deletions enginetest/queries/transaction_queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,11 @@ var TransactionTests = []TransactionTest{
Query: "/* client a */ select * from t2",
Expected: []sql.Row{{0, 0, nil}},
},

{
Query: "/* client a */ START TRANSACTION READ ONLY",
Expected: []sql.Row{},
},
{
Query: "/* client a */ create temporary table tmp2(pk int primary key)",
ExpectedErr: sql.ErrReadOnlyTransaction,
Expand Down Expand Up @@ -1064,4 +1069,261 @@ var TransactionTests = []TransactionTest{
},
},
},
{
Name: "create table queries are implicitly committed",
Assertions: []ScriptTestAssertion{
{
Query: "/* client a */ set @@autocommit = 0;",
Expected: []sql.Row{{}},
},
{
Query: "/* client a */ start transaction;",
Expected: []sql.Row{},
},
{
// This implicitly commits the transaction
Query: "/* client a */ create table t (pk int primary key);",
Expected: []sql.Row{{types.OkResult{}}},
},
{
Query: "/* client b */ select * from t;",
Expected: []sql.Row{},
},

{
Query: "/* client a */ start transaction;",
Expected: []sql.Row{},
},
{
Query: "/* client a */ insert into t values (1), (2), (3);",
Expected: []sql.Row{{types.OkResult{RowsAffected: 3}}},
},
{
Query: "/* client b */ select * from t;",
Expected: []sql.Row{},
},
{
// This implicitly commits the transaction
Query: "/* client a */ create table t2 (pk int primary key);",
Expected: []sql.Row{{types.OkResult{}}},
},
{
Query: "/* client b */ select * from t;",
Expected: []sql.Row{
{1},
{2},
{3},
},
},
},
},
{
Name: "alter table queries are implicitly committed",
Assertions: []ScriptTestAssertion{
{
Query: "/* client a */ set @@autocommit = 0;",
Expected: []sql.Row{{}},
},
{
Query: "/* client a */ start transaction;",
Expected: []sql.Row{},
},
{
// This implicitly commits the transaction
Query: "/* client a */ create table t (pk int primary key);",
Expected: []sql.Row{{types.OkResult{}}},
},
{
Query: "/* client b */ show create table t;",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n" +
" `pk` int NOT NULL,\n" +
" PRIMARY KEY (`pk`)\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},

{
Query: "/* client a */ start transaction;",
Expected: []sql.Row{},
},
{
Query: "/* client a */ alter table t add column i int;",
Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
},
{
Query: "/* client b */ show create table t;",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n" +
" `pk` int NOT NULL,\n" +
" `i` int,\n" +
" PRIMARY KEY (`pk`)\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
},
},
{
Name: "create index queries are implicitly committed",
Assertions: []ScriptTestAssertion{
{
Query: "/* client a */ set @@autocommit = 0;",
Expected: []sql.Row{{}},
},
{
Query: "/* client a */ start transaction;",
Expected: []sql.Row{},
},
{
// This implicitly commits the transaction
Query: "/* client a */ create table t (i int primary key);",
Expected: []sql.Row{{types.OkResult{}}},
},
{
Query: "/* client b */ show create table t;",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n" +
" `i` int NOT NULL,\n" +
" PRIMARY KEY (`i`)\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},

{
Query: "/* client a */ start transaction;",
Expected: []sql.Row{},
},
{
Query: "/* client a */ create unique index idx on t (i);",
Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
},
{
Query: "/* client b */ show create table t;",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n" +
" `i` int NOT NULL,\n" +
" PRIMARY KEY (`i`),\n" +
" UNIQUE KEY `idx` (`i`)\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},
},
},
{
Name: "create trigger queries are implicitly committed",
Assertions: []ScriptTestAssertion{
{
Query: "/* client a */ set @@autocommit = 0;",
Expected: []sql.Row{{}},
},
{
Query: "/* client a */ start transaction;",
Expected: []sql.Row{},
},
{
// This implicitly commits the transaction
Query: "/* client a */ create table t (i int primary key);",
Expected: []sql.Row{{types.OkResult{}}},
},
{
Query: "/* client b */ show create table t;",
Expected: []sql.Row{{"t", "CREATE TABLE `t` (\n" +
" `i` int NOT NULL,\n" +
" PRIMARY KEY (`i`)\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
},

{
Query: "/* client a */ start transaction;",
Expected: []sql.Row{},
},
{
Query: "/* client a */ create trigger trig before insert on t for each row set i = 0;",
Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
},
{
Query: "/* client b */ select trigger_schema, trigger_name from information_schema.triggers where trigger_name = 'trig';",
Expected: []sql.Row{{"mydb", "trig"}},
},
},
},
{
Name: "create view queries are implicitly committed",
Assertions: []ScriptTestAssertion{
{
Query: "/* client a */ set @@autocommit = 0;",
Expected: []sql.Row{{}},
},
{
Query: "/* client a */ start transaction;",
Expected: []sql.Row{},
},
{
Query: "/* client a */ create view v as select 1;",
Expected: []sql.Row{{types.OkResult{RowsAffected: 0}}},
},
{
Query: "/* client b */ show create view v;",
Expected: []sql.Row{{"v", "CREATE VIEW `v` AS select 1", "utf8mb4", "utf8mb4_0900_bin"}},
},
},
},
{
Name: "create procedure queries are implicitly committed",
Assertions: []ScriptTestAssertion{
{
Query: "/* client a */ set @@autocommit = 0;",
Expected: []sql.Row{{}},
},
{
Query: "/* client a */ start transaction;",
Expected: []sql.Row{},
},
{
// This implicitly commits the transaction
Query: "/* client a */ create procedure p() begin select 1; end;",
Expected: []sql.Row{{types.OkResult{}}},
},
{
Query: "/* client b */ show create procedure p;",
Expected: []sql.Row{{"p", "", "/* client a */ create procedure p() begin select 1; end", "utf8mb4", "utf8mb4_0900_bin", "utf8mb4_0900_bin"}},
},
},
},
{
Name: "create procedure queries are implicitly committed",
Assertions: []ScriptTestAssertion{
{
Query: "/* client a */ set @@autocommit = 0;",
Expected: []sql.Row{{}},
},
{
Query: "/* client a */ start transaction;",
Expected: []sql.Row{},
},
{
// This implicitly commits the transaction
Query: "/* client a */ create event e on schedule every 1 second starts '2025-01-01' do begin select 1; end;",
Expected: []sql.Row{{types.OkResult{}}},
},
{
Query: "/* client b */ show create event e;",
Expected: []sql.Row{{"e", "NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES", "SYSTEM", "CREATE DEFINER = `root`@`localhost` EVENT `e` ON SCHEDULE EVERY 1 SECOND STARTS '2025-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO begin select 1; end", "utf8mb4", "utf8mb4_0900_bin", "utf8mb4_0900_bin"}},
},
},
},
{
Name: "create database queries are implicitly committed",
Assertions: []ScriptTestAssertion{
{
Query: "/* client a */ set @@autocommit = 0;",
Expected: []sql.Row{{}},
},
{
Query: "/* client a */ start transaction;",
Expected: []sql.Row{},
},
{
// This implicitly commits the transaction
Query: "/* client a */ create database otherdb;",
Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
},
{
Query: "/* client b */ show create database otherdb;",
Expected: []sql.Row{{"otherdb", "CREATE DATABASE `otherdb` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin */"}},
},
},
},
}
2 changes: 2 additions & 0 deletions sql/planbuilder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,13 @@ func (b *Builder) buildSubquery(inScope *scope, stmt ast.Statement, subQuery str
case *ast.Show:
return b.buildShow(inScope, n)
case *ast.DDL:
b.qFlags.Set(sql.QFlagDDL)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good start but incomplete. There are other DDL operations that aren't captured by this node, notably ALTER TABLE

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should add more tests for other statement types

Copy link
Contributor Author

@jycor jycor Jan 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Included QFlagAlterTable and added tests for create index, create database, create procedure, create event, alter table

return b.buildDDL(inScope, subQuery, fullQuery, n)
case *ast.AlterTable:
b.qFlags.Set(sql.QFlagAlterTable)
return b.buildAlterTable(inScope, subQuery, n)
case *ast.DBDDL:
b.qFlags.Set(sql.QFlagDBDDL)
return b.buildDBDDL(inScope, n)
case *ast.Explain:
return b.buildExplain(inScope, n)
Expand Down
2 changes: 2 additions & 0 deletions sql/query_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const (
QFlgNotExpr
QFlagCount
QFlagCountStar
QFlagDDL
QFlagDBDDL
QFlagAlterTable
QFlagCrossJoin
QFlagSort
Expand Down
10 changes: 7 additions & 3 deletions sql/rowexec/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,16 @@ func NewOverrideBuilder(override sql.NodeExecBuilder) sql.NodeExecBuilder {
}

// FinalizeIters applies the final transformations on sql.RowIter before execution.
func FinalizeIters(ctx *sql.Context, analyzed sql.Node, qFlags *sql.QueryFlags, iter sql.RowIter) (sql.RowIter, sql.Schema) {
func FinalizeIters(ctx *sql.Context, analyzed sql.Node, qFlags *sql.QueryFlags, iter sql.RowIter) (sql.RowIter, sql.Schema, error) {
var sch sql.Schema
var err error
iter, sch = AddAccumulatorIter(ctx, iter)
iter = AddTriggerRollbackIter(ctx, qFlags, iter)
iter = AddTransactionCommittingIter(qFlags, iter)
iter, err = AddTransactionCommittingIter(ctx, qFlags, iter)
if err != nil {
return nil, nil, err
}
iter = plan.AddTrackedRowIter(ctx, analyzed, iter)
iter = AddExpressionCloser(analyzed, iter)
return iter, sch
return iter, sch, nil
}
Loading
Loading