Skip to content

Commit

Permalink
Merge branch 'master' into meiji163/parallel-repl
Browse files Browse the repository at this point in the history
  • Loading branch information
meiji163 committed Dec 19, 2024
2 parents e1ca9cd + 0fe1190 commit b34f2e2
Show file tree
Hide file tree
Showing 150 changed files with 3,099 additions and 1,670 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
/libexec/
/.vendor/
.idea/
*.tmp
10 changes: 5 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ require (
github.com/stretchr/testify v1.9.0
github.com/testcontainers/testcontainers-go v0.34.0
golang.org/x/net v0.24.0
golang.org/x/sync v0.8.0
golang.org/x/term v0.19.0
golang.org/x/text v0.14.0
golang.org/x/sync v0.10.0
golang.org/x/term v0.27.0
golang.org/x/text v0.21.0
)

require (
Expand Down Expand Up @@ -66,7 +66,7 @@ require (
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/sys v0.28.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
Expand All @@ -199,8 +199,8 @@ golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand All @@ -211,15 +211,15 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
Expand Down
1 change: 1 addition & 0 deletions go/base/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ type MigrationContext struct {
CurrentLag int64
currentProgress uint64
etaNanoseonds int64
EtaRowsPerSecond int64
ThrottleHTTPIntervalMillis int64
ThrottleHTTPStatusCode int64
ThrottleHTTPTimeoutMillis int64
Expand Down
2 changes: 1 addition & 1 deletion go/cmd/gh-ost/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func main() {
chunkSize := flag.Int64("chunk-size", 1000, "amount of rows to handle in each iteration (allowed range: 10-100,000)")
dmlBatchSize := flag.Int64("dml-batch-size", 10, "batch size for DML events to apply in a single transaction (range 1-100)")
defaultRetries := flag.Int64("default-retries", 60, "Default number of retries for various operations before panicking")
cutOverLockTimeoutSeconds := flag.Int64("cut-over-lock-timeout-seconds", 3, "Max number of seconds to hold locks on tables while attempting to cut-over (retry attempted when lock exceeds timeout)")
cutOverLockTimeoutSeconds := flag.Int64("cut-over-lock-timeout-seconds", 3, "Max number of seconds to hold locks on tables while attempting to cut-over (retry attempted when lock exceeds timeout) or attempting instant DDL")
niceRatio := flag.Float64("nice-ratio", 0, "force being 'nice', imply sleep time per chunk time; range: [0.0..100.0]. Example values: 0 is aggressive. 1: for every 1ms spent copying rows, sleep additional 1ms (effectively doubling runtime); 0.7: for every 10ms spend in a rowcopy chunk, spend 7ms sleeping immediately after")
flag.IntVar(&migrationContext.NumWorkers, "workers", 8, "Number of concurrent workers for applying DML events. Each worker uses one goroutine.")

Expand Down
9 changes: 9 additions & 0 deletions go/logic/applier.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,15 @@ func (this *Applier) ValidateOrDropExistingTables() error {
func (this *Applier) AttemptInstantDDL() error {
query := this.generateInstantDDLQuery()
this.migrationContext.Log.Infof("INSTANT DDL query is: %s", query)

// Reuse cut-over-lock-timeout from regular migration process to reduce risk
// in situations where there may be long-running transactions.
tableLockTimeoutSeconds := this.migrationContext.CutOverLockTimeoutSeconds * 2
this.migrationContext.Log.Infof("Setting LOCK timeout as %d seconds", tableLockTimeoutSeconds)
lockTimeoutQuery := fmt.Sprintf(`set /* gh-ost */ session lock_wait_timeout:=%d`, tableLockTimeoutSeconds)
if _, err := this.db.Exec(lockTimeoutQuery); err != nil {
return err
}
// We don't need a trx, because for instant DDL the SQL mode doesn't matter.
_, err := this.db.Exec(query)
return err
Expand Down
47 changes: 45 additions & 2 deletions go/logic/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import (
"github.com/openark/golib/sqlutils"
)

const startSlavePostWaitMilliseconds = 500 * time.Millisecond
const startReplicationPostWait = 250 * time.Millisecond
const startReplicationMaxWait = 2 * time.Second

// Inspector reads data from the read-MySQL-server (typically a replica, but can be the master)
// It is used for gaining initial status and structure, and later also follow up on progress and changelog
Expand Down Expand Up @@ -302,12 +303,50 @@ func (this *Inspector) restartReplication() error {
if startError != nil {
return startError
}
time.Sleep(startSlavePostWaitMilliseconds)

// loop until replication is running unless we hit a max timeout.
startTime := time.Now()
for {
replicationRunning, err := this.validateReplicationRestarted()
if err != nil {
return fmt.Errorf("Failed to validate if replication had been restarted: %w", err)
}
if replicationRunning {
break
}
if time.Since(startTime) > startReplicationMaxWait {
return fmt.Errorf("Replication did not restart within the maximum wait time of %s", startReplicationMaxWait)
}
this.migrationContext.Log.Debugf("Replication not yet restarted, waiting...")
time.Sleep(startReplicationPostWait)
}

this.migrationContext.Log.Debugf("Replication restarted")
return nil
}

// validateReplicationRestarted checks that the Slave_IO_Running and Slave_SQL_Running are both 'Yes'
// returns true if both are 'Yes', false otherwise
func (this *Inspector) validateReplicationRestarted() (bool, error) {
errNotRunning := fmt.Errorf("Replication not running on %s", this.connectionConfig.Key.String())
query := `show /* gh-ost */ slave status`
err := sqlutils.QueryRowsMap(this.db, query, func(rowMap sqlutils.RowMap) error {
if rowMap.GetString("Slave_IO_Running") != "Yes" || rowMap.GetString("Slave_SQL_Running") != "Yes" {
return errNotRunning
}
return nil
})

if err != nil {
// If the error is that replication is not running, return that and not an error
if errors.Is(err, errNotRunning) {
return false, nil
}
return false, err
}
return true, nil
}

// applyBinlogFormat sets ROW binlog format and restarts replication to make
// the replication thread apply it.
func (this *Inspector) applyBinlogFormat() error {
Expand Down Expand Up @@ -589,6 +628,7 @@ func (this *Inspector) applyColumnTypes(databaseName, tableName string, columnsL
columnName := m.GetString("COLUMN_NAME")
columnType := m.GetString("COLUMN_TYPE")
columnOctetLength := m.GetUint("CHARACTER_OCTET_LENGTH")
extra := m.GetString("EXTRA")
for _, columnsList := range columnsLists {
column := columnsList.GetColumn(columnName)
if column == nil {
Expand Down Expand Up @@ -621,6 +661,9 @@ func (this *Inspector) applyColumnTypes(databaseName, tableName string, columnsL
column.Type = sql.BinaryColumnType
column.BinaryOctetLength = columnOctetLength
}
if strings.Contains(extra, " GENERATED") {
column.IsVirtual = true
}
if charset := m.GetString("CHARACTER_SET_NAME"); charset != "" {
column.Charset = charset
}
Expand Down
24 changes: 21 additions & 3 deletions go/logic/migrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -826,11 +826,18 @@ func (this *Migrator) initiateStatus() {
this.printStatus(ForcePrintStatusAndHintRule)
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
var previousCount int64
for range ticker.C {
if atomic.LoadInt64(&this.finishedMigrating) > 0 {
return
}
go this.printStatus(HeuristicPrintStatusRule)
totalCopied := atomic.LoadInt64(&this.migrationContext.TotalRowsCopied)
if previousCount > 0 {
copiedThisLoop := totalCopied - previousCount
atomic.StoreInt64(&this.migrationContext.EtaRowsPerSecond, copiedThisLoop)
}
previousCount = totalCopied
}
}

Expand Down Expand Up @@ -932,9 +939,20 @@ func (this *Migrator) getMigrationETA(rowsEstimate int64) (eta string, duration
duration = 0
} else if progressPct >= 0.1 {
totalRowsCopied := this.migrationContext.GetTotalRowsCopied()
elapsedRowCopySeconds := this.migrationContext.ElapsedRowCopyTime().Seconds()
totalExpectedSeconds := elapsedRowCopySeconds * float64(rowsEstimate) / float64(totalRowsCopied)
etaSeconds := totalExpectedSeconds - elapsedRowCopySeconds
etaRowsPerSecond := atomic.LoadInt64(&this.migrationContext.EtaRowsPerSecond)
var etaSeconds float64
// If there is data available on our current row-copies-per-second rate, use it.
// Otherwise we can fallback to the total elapsed time and extrapolate.
// This is going to be less accurate on a longer copy as the insert rate
// will tend to slow down.
if etaRowsPerSecond > 0 {
remainingRows := float64(rowsEstimate) - float64(totalRowsCopied)
etaSeconds = remainingRows / float64(etaRowsPerSecond)
} else {
elapsedRowCopySeconds := this.migrationContext.ElapsedRowCopyTime().Seconds()
totalExpectedSeconds := elapsedRowCopySeconds * float64(rowsEstimate) / float64(totalRowsCopied)
etaSeconds = totalExpectedSeconds - elapsedRowCopySeconds
}
if etaSeconds >= 0 {
duration = time.Duration(etaSeconds) * time.Second
} else {
Expand Down
9 changes: 9 additions & 0 deletions go/logic/migrator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,15 @@ func TestMigratorGetMigrationStateAndETA(t *testing.T) {
require.Equal(t, "4h29m44s", eta)
require.Equal(t, "4h29m44s", etaDuration.String())
}
{
// Test using rows-per-second added data.
migrationContext.TotalRowsCopied = 456
migrationContext.EtaRowsPerSecond = 100
state, eta, etaDuration := migrator.getMigrationStateAndETA(123456)
require.Equal(t, "migrating", state)
require.Equal(t, "20m30s", eta)
require.Equal(t, "20m30s", etaDuration.String())
}
{
migrationContext.TotalRowsCopied = 456
state, eta, etaDuration := migrator.getMigrationStateAndETA(456)
Expand Down
11 changes: 6 additions & 5 deletions go/sql/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,12 @@ func NewDMLUpdateQueryBuilder(databaseName, tableName string, tableColumns, shar
if uniqueKeyColumns.Len() == 0 {
return nil, fmt.Errorf("no unique key columns found in NewDMLUpdateQueryBuilder")
}
// If unique key contains virtual columns, those column won't be in sharedColumns
// which only contains non-virtual columns
nonVirtualUniqueKeyColumns := uniqueKeyColumns.FilterBy(func(column Column) bool { return !column.IsVirtual })
if !nonVirtualUniqueKeyColumns.IsSubsetOf(sharedColumns) {
return nil, fmt.Errorf("unique key columns is not a subset of shared columns in NewDMLUpdateQueryBuilder")
}
databaseName = EscapeName(databaseName)
tableName = EscapeName(tableName)
setClause, err := BuildSetPreparedClause(mappedSharedColumns)
Expand Down Expand Up @@ -580,11 +586,6 @@ func NewDMLUpdateQueryBuilder(databaseName, tableName string, tableColumns, shar
// BuildQuery builds the arguments array for a DML event UPDATE query.
// It returns the query string, the shared arguments array, and the unique key arguments array.
func (b *DMLUpdateQueryBuilder) BuildQuery(valueArgs, whereArgs []interface{}) (string, []interface{}, []interface{}, error) {
// TODO: move this check back to `NewDMLUpdateQueryBuilder()`, needs fix on generated columns.
if !b.uniqueKeyColumns.IsSubsetOf(b.sharedColumns) {
return "", nil, nil, fmt.Errorf("unique key columns is not a subset of shared columns in DMLUpdateQueryBuilder")
}

sharedArgs := make([]interface{}, 0, b.sharedColumns.Len())
for _, column := range b.sharedColumns.Columns() {
tableOrdinal := b.tableColumns.Ordinals[column.Name]
Expand Down
4 changes: 1 addition & 3 deletions go/sql/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -688,9 +688,7 @@ func TestBuildDMLUpdateQuery(t *testing.T) {
{
sharedColumns := NewColumnList([]string{"id", "name", "position", "age"})
uniqueKeyColumns := NewColumnList([]string{"age", "surprise"})
builder, err := NewDMLUpdateQueryBuilder(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, uniqueKeyColumns)
require.NoError(t, err)
_, _, _, err = builder.BuildQuery(valueArgs, whereArgs)
_, err := NewDMLUpdateQueryBuilder(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, uniqueKeyColumns)
require.Error(t, err)
}
{
Expand Down
11 changes: 11 additions & 0 deletions go/sql/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type CharacterSetConversion struct {
type Column struct {
Name string
IsUnsigned bool
IsVirtual bool
Charset string
Type ColumnType
EnumValues string
Expand Down Expand Up @@ -244,6 +245,16 @@ func (this *ColumnList) IsSubsetOf(other *ColumnList) bool {
return true
}

func (this *ColumnList) FilterBy(f func(Column) bool) *ColumnList {
filteredCols := make([]Column, 0, len(this.columns))
for _, column := range this.columns {
if f(column) {
filteredCols = append(filteredCols, column)
}
}
return &ColumnList{Ordinals: this.Ordinals, columns: filteredCols}
}

func (this *ColumnList) Len() int {
return len(this.columns)
}
Expand Down
16 changes: 5 additions & 11 deletions localtests/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,31 +1,25 @@
services:
mysql-primary:
image: mysql:8.0.39
image: $TEST_MYSQL_IMAGE
container_name: mysql-primary
command: --server-id=1 --log-bin=mysql-bin --binlog-format=row --gtid-mode=ON --enforce-gtid-consistency=ON
# volumes:
# - /var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: opensesame
MYSQL_ROOT_HOST: '%'
MYSQL_DATABASE: test
MYSQL_USER: repl
MYSQL_PASSWORD: repl
MYSQL_TCP_PORT: 3307
ports:
- '3307:3307'
expose:
- '3307'
mysql-replica:
image: mysql:8.0.39
image: $TEST_MYSQL_IMAGE
container_name: mysql-replica
command: --server-id=2 --log-bin=mysql-bin --binlog-format=row --gtid-mode=ON --enforce-gtid-consistency=ON
# volumes:
# - /var/lib/mysql
command: --server-id=2 --log-bin=mysql-bin --binlog-format=row --gtid-mode=ON --enforce-gtid-consistency=ON --log-slave-updates=ON
environment:
MYSQL_ROOT_PASSWORD: opensesame
MYSQL_ROOT_HOST: '%'
MYSQL_DATABASE: test
MYSQL_USER: repl
MYSQL_PASSWORD: repl
MYSQL_TCP_PORT: 3308
ports:
- '3308:3308'
Expand Down
10 changes: 8 additions & 2 deletions localtests/generated-columns-unique/create.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ create table gh_ost_test (
id int auto_increment,
`idb` varchar(36) CHARACTER SET utf8mb4 GENERATED ALWAYS AS (json_unquote(json_extract(`jsonobj`,_utf8mb4'$._id'))) STORED NOT NULL,
`jsonobj` json NOT NULL,
updated datetime DEFAULT NULL,
PRIMARY KEY (`id`,`idb`)
) auto_increment=1;

Expand All @@ -25,6 +26,11 @@ begin
insert into gh_ost_test (id, jsonobj) values (null, '{"_id":13}');
insert into gh_ost_test (id, jsonobj) values (null, '{"_id":17}');
insert into gh_ost_test (id, jsonobj) values (null, '{"_id":19}');
insert into gh_ost_test (id, jsonobj) values (null, '{"_id":23}');
insert into gh_ost_test (id, jsonobj) values (null, '{"_id":27}');

update gh_ost_test set updated=NOW() where idb=5;
update gh_ost_test set updated=NOW() where idb=7;
update gh_ost_test set updated=NOW() where idb=11;
update gh_ost_test set updated=NOW() where idb=13;
update gh_ost_test set updated=NOW() where idb=17;
update gh_ost_test set updated=NOW() where idb=19;
end ;;
2 changes: 2 additions & 0 deletions localtests/generated-columns/create.sql
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ begin
insert into gh_ost_test (id, a, b) values (null, 2,0);
insert into gh_ost_test (id, a, b) values (null, 2,1);
insert into gh_ost_test (id, a, b) values (null, 2,2);
update gh_ost_test set b=b+1 where id < 5;
update gh_ost_test set b=b-1 where id >= 5;
end ;;
Loading

0 comments on commit b34f2e2

Please sign in to comment.