Skip to content
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

GENERATED column as part of PK: handling UPDATE scenario #1004

Closed
wants to merge 53 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
9b2a04d
Merge pull request #2 from openark/workflow-upload-artifact
shlomi-noach Jul 28, 2020
9ccde4f
Merge pull request #5 from openark/parse-alter-statement
shlomi-noach Jul 29, 2020
b59a8ed
merged conflict
shlomi-noach Aug 2, 2020
6012e80
Merge pull request #8 from openark/ajm188-handle_driver_timeout_error
shlomi-noach Aug 2, 2020
ae22d84
v1.1.0
shlomi-noach Aug 5, 2020
ca0ca5a
Merge remote-tracking branch 'upstream/master' into updates-from-upst…
shlomi-noach Oct 18, 2020
e9f9af2
Merge pull request #11 from openark/updates-from-upstream-2020-10
shlomi-noach Oct 18, 2020
294d43b
WIP: copying AUTO_INCREMENT value to ghost table
shlomi-noach Dec 31, 2020
26f7602
greping for 'expect_table_structure' content
shlomi-noach Dec 31, 2020
75009db
Adding simple test for 'expect_table_structure' scenario
shlomi-noach Dec 31, 2020
eeab264
adding tests for AUTO_INCREMENT value after row deletes. Should initi…
shlomi-noach Dec 31, 2020
2d0281f
clear event beforehand
shlomi-noach Dec 31, 2020
af20211
parsing AUTO_INCREMENT from alter query, reading AUTO_INCREMENT from …
shlomi-noach Dec 31, 2020
31069ae
support GetUint64
shlomi-noach Dec 31, 2020
3d4dfaa
minor update to test
shlomi-noach Dec 31, 2020
63219ab
adding test for user defined AUTO_INCREMENT statement
shlomi-noach Dec 31, 2020
525a80d
Merge branch 'master' into copy-auto-increment
shlomi-noach Jan 5, 2021
ff82140
Merge pull request #12 from openark/copy-auto-increment
shlomi-noach Jan 5, 2021
7202076
Generated column as part of UNIQUE (or PRIMARY) KEY
shlomi-noach Jan 19, 2021
b7b3bfb
skip analysis of generated column data type in unique key
shlomi-noach Jan 19, 2021
253658d
Merge pull request #13 from openark/unique-key-generated-column
shlomi-noach Jan 27, 2021
4a36e24
Merge pull request #14 from ccoffey/cathal/safer_cut_over
shlomi-noach Feb 7, 2021
710c9dd
All MySQL DBs limited to max 3 concurrent/idle connections
shlomi-noach Feb 18, 2021
dea8d54
Merge branch 'master' into limit-mysql-connetions
shlomi-noach Feb 22, 2021
2b5cf78
Merge pull request #15 from openark/limit-mysql-connetions
shlomi-noach Feb 22, 2021
54000ab
hooks: reporting GH_OST_ETA_SECONDS. ETA stored as part of migration …
shlomi-noach Mar 7, 2021
51719a2
GH_OST_ETA_NANOSECONDS
shlomi-noach Mar 7, 2021
76b9c16
N/A denoted by negative value
shlomi-noach Mar 7, 2021
b688c58
ETAUnknown constant
shlomi-noach Mar 7, 2021
33516f4
Merge pull request #17 from openark/hooks-eta-seconds
shlomi-noach Mar 7, 2021
c1bfe94
Convering enum to varchar
shlomi-noach May 2, 2021
9bb2daa
test: not null
shlomi-noach May 2, 2021
939b898
first attempt at setting enum-to-string right
shlomi-noach May 2, 2021
95ee9e2
fix insert query
shlomi-noach May 2, 2021
e80ddb4
store enum values, use when populating
shlomi-noach May 2, 2021
6e5b665
apply EnumValues to mapped column
shlomi-noach May 2, 2021
82bdf06
fix compilation error
shlomi-noach May 2, 2021
404ca81
Merge pull request #18 from openark/enum-to-varchar
shlomi-noach May 3, 2021
2613554
v1.1.3
shlomi-noach May 3, 2021
5e19d87
merge from upstream, resolve conflicts
shlomi-noach May 31, 2021
1876a90
Merge pull request #21 from openark/fetch-upstream-202105
shlomi-noach May 31, 2021
47f14a4
Only allow -switch-to-rbr when inspecting replica
shlomi-noach Jun 20, 2021
c1b0797
Fetch from upstream, 2021-06-27
shlomi-noach Jun 27, 2021
049d452
Merge pull request #25 from openark/from-upstream-20210627
shlomi-noach Jun 27, 2021
54ba3cb
Merge branch 'master' into switch-to-rbr-only-on-replica
shlomi-noach Jun 27, 2021
28f7f19
Merge pull request #23 from openark/switch-to-rbr-only-on-replica
shlomi-noach Jun 29, 2021
faedede
GENERATED colunm as part of PK: handling UPDATE scenario
shlomi-noach Jul 6, 2021
ed9a91d
non-null timestamp
shlomi-noach Jul 6, 2021
bce9e39
use last_insert_id
shlomi-noach Jul 6, 2021
fa87c1d
Revert change
shlomi-noach Jul 6, 2021
4610fa7
' GENERATED' was correct, after all
shlomi-noach Jul 8, 2021
bb06e81
compute sharedVirtualColumns and take it into account while sanity-te…
shlomi-noach Jul 8, 2021
e09694d
fix unit tests
shlomi-noach Jul 8, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go/base/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ type MigrationContext struct {
GhostTableUniqueKeys [](*sql.UniqueKey)
UniqueKey *sql.UniqueKey
SharedColumns *sql.ColumnList
SharedVirtualColumns *sql.ColumnList
ColumnRenameMap map[string]string
DroppedColumnsMap map[string]bool
MappedSharedColumns *sql.ColumnList
Expand Down
2 changes: 1 addition & 1 deletion go/logic/applier.go
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,7 @@ func (this *Applier) buildDMLEventQuery(dmlEvent *binlog.BinlogDMLEvent) (result
results = append(results, this.buildDMLEventQuery(dmlEvent)...)
return results
}
query, sharedArgs, uniqueKeyArgs, err := sql.BuildDMLUpdateQuery(dmlEvent.DatabaseName, this.migrationContext.GetGhostTableName(), this.migrationContext.OriginalTableColumns, this.migrationContext.SharedColumns, this.migrationContext.MappedSharedColumns, &this.migrationContext.UniqueKey.Columns, dmlEvent.NewColumnValues.AbstractValues(), dmlEvent.WhereColumnValues.AbstractValues())
query, sharedArgs, uniqueKeyArgs, err := sql.BuildDMLUpdateQuery(dmlEvent.DatabaseName, this.migrationContext.GetGhostTableName(), this.migrationContext.OriginalTableColumns, this.migrationContext.SharedColumns, this.migrationContext.MappedSharedColumns, this.migrationContext.SharedVirtualColumns, &this.migrationContext.UniqueKey.Columns, dmlEvent.NewColumnValues.AbstractValues(), dmlEvent.WhereColumnValues.AbstractValues())
args := sqlutils.Args()
args = append(args, sharedArgs...)
args = append(args, uniqueKeyArgs...)
Expand Down
30 changes: 27 additions & 3 deletions go/logic/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ func (this *Inspector) inspectOriginalAndGhostTables() (err error) {
}
}

this.migrationContext.SharedColumns, this.migrationContext.MappedSharedColumns = this.getSharedColumns(this.migrationContext.OriginalTableColumns, this.migrationContext.GhostTableColumns, this.migrationContext.OriginalTableVirtualColumns, this.migrationContext.GhostTableVirtualColumns, this.migrationContext.ColumnRenameMap)
this.migrationContext.SharedColumns, this.migrationContext.MappedSharedColumns, this.migrationContext.SharedVirtualColumns = this.getSharedColumns(this.migrationContext.OriginalTableColumns, this.migrationContext.GhostTableColumns, this.migrationContext.OriginalTableVirtualColumns, this.migrationContext.GhostTableVirtualColumns, this.migrationContext.ColumnRenameMap)
this.migrationContext.Log.Infof("Shared columns are %s", this.migrationContext.SharedColumns)
// By fact that a non-empty unique key exists we also know the shared columns are non-empty

Expand Down Expand Up @@ -720,8 +720,9 @@ func (this *Inspector) getSharedUniqueKeys(originalUniqueKeys, ghostUniqueKeys [
}

// getSharedColumns returns the intersection of two lists of columns in same order as the first list
func (this *Inspector) getSharedColumns(originalColumns, ghostColumns *sql.ColumnList, originalVirtualColumns, ghostVirtualColumns *sql.ColumnList, columnRenameMap map[string]string) (*sql.ColumnList, *sql.ColumnList) {
func (this *Inspector) getSharedColumns(originalColumns, ghostColumns *sql.ColumnList, originalVirtualColumns, ghostVirtualColumns *sql.ColumnList, columnRenameMap map[string]string) (*sql.ColumnList, *sql.ColumnList, *sql.ColumnList) {
sharedColumnNames := []string{}
sharedVirtualColumnNames := []string{}
for _, originalColumn := range originalColumns.Names() {
isSharedColumn := false
for _, ghostColumn := range ghostColumns.Names() {
Expand Down Expand Up @@ -754,6 +755,29 @@ func (this *Inspector) getSharedColumns(originalColumns, ghostColumns *sql.Colum
sharedColumnNames = append(sharedColumnNames, originalColumn)
}
}
// Virtual columns
for _, originalColumn := range originalVirtualColumns.Names() {
isSharedColumn := false
for _, ghostColumn := range ghostVirtualColumns.Names() {
if strings.EqualFold(originalColumn, ghostColumn) {
isSharedColumn = true
break
}
if strings.EqualFold(columnRenameMap[originalColumn], ghostColumn) {
isSharedColumn = true
break
}
}
for droppedColumn := range this.migrationContext.DroppedColumnsMap {
if strings.EqualFold(originalColumn, droppedColumn) {
isSharedColumn = false
break
}
}
if isSharedColumn {
sharedVirtualColumnNames = append(sharedVirtualColumnNames, originalColumn)
}
}
mappedSharedColumnNames := []string{}
for _, columnName := range sharedColumnNames {
if mapped, ok := columnRenameMap[columnName]; ok {
Expand All @@ -762,7 +786,7 @@ func (this *Inspector) getSharedColumns(originalColumns, ghostColumns *sql.Colum
mappedSharedColumnNames = append(mappedSharedColumnNames, columnName)
}
}
return sql.NewColumnList(sharedColumnNames), sql.NewColumnList(mappedSharedColumnNames)
return sql.NewColumnList(sharedColumnNames), sql.NewColumnList(mappedSharedColumnNames), sql.NewColumnList(sharedVirtualColumnNames)
}

// showCreateTable returns the `show create table` statement for given table
Expand Down
3 changes: 3 additions & 0 deletions go/logic/migrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,9 @@ func (this *Migrator) initiateInspector() (err error) {
} else if this.migrationContext.InspectorIsAlsoApplier() && !this.migrationContext.AllowedRunningOnMaster {
return fmt.Errorf("It seems like this migration attempt to run directly on master. Preferably it would be executed on a replica (and this reduces load from the master). To proceed please provide --allow-on-master. Inspector config=%+v, applier config=%+v", this.migrationContext.InspectorConnectionConfig, this.migrationContext.ApplierConnectionConfig)
}
if this.migrationContext.InspectorIsAlsoApplier() && this.migrationContext.SwitchToRowBinlogFormat {
return fmt.Errorf("--switch-to-rbr is only allowed when inspecting a replica. This migration seems to run on the primary %+v", *this.migrationContext.ApplierConnectionConfig.ImpliedKey)
}
if err := this.inspector.validateLogSlaveUpdates(); err != nil {
return err
}
Expand Down
6 changes: 4 additions & 2 deletions go/sql/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,9 @@ func BuildDMLInsertQuery(databaseName, tableName string, tableColumns, sharedCol
return result, sharedArgs, nil
}

func BuildDMLUpdateQuery(databaseName, tableName string, tableColumns, sharedColumns, mappedSharedColumns, uniqueKeyColumns *ColumnList, valueArgs, whereArgs []interface{}) (result string, sharedArgs, uniqueKeyArgs []interface{}, err error) {
func BuildDMLUpdateQuery(databaseName, tableName string,
tableColumns, sharedColumns, mappedSharedColumns, sharedVirtualColumns, uniqueKeyColumns *ColumnList,
valueArgs, whereArgs []interface{}) (result string, sharedArgs, uniqueKeyArgs []interface{}, err error) {
if len(valueArgs) != tableColumns.Len() {
return result, sharedArgs, uniqueKeyArgs, fmt.Errorf("value args count differs from table column count in BuildDMLUpdateQuery")
}
Expand All @@ -471,7 +473,7 @@ func BuildDMLUpdateQuery(databaseName, tableName string, tableColumns, sharedCol
if !sharedColumns.IsSubsetOf(tableColumns) {
return result, sharedArgs, uniqueKeyArgs, fmt.Errorf("shared columns is not a subset of table columns in BuildDMLUpdateQuery")
}
if !uniqueKeyColumns.IsSubsetOf(sharedColumns) {
if !uniqueKeyColumns.IsSubsetOf(ConcatenateColumnList(sharedColumns, sharedVirtualColumns)) {
return result, sharedArgs, uniqueKeyArgs, fmt.Errorf("unique key columns is not a subset of shared columns in BuildDMLUpdateQuery")
}
if sharedColumns.Len() == 0 {
Expand Down
26 changes: 17 additions & 9 deletions go/sql/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,8 @@ func TestBuildDMLUpdateQuery(t *testing.T) {
{
sharedColumns := NewColumnList([]string{"id", "name", "position", "age"})
uniqueKeyColumns := NewColumnList([]string{"position"})
query, sharedArgs, uniqueKeyArgs, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, uniqueKeyColumns, valueArgs, whereArgs)
generatedColumns := NewColumnList([]string{})
query, sharedArgs, uniqueKeyArgs, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, generatedColumns, uniqueKeyColumns, valueArgs, whereArgs)
test.S(t).ExpectNil(err)
expected := `
update /* gh-ost mydb.tbl */
Expand All @@ -560,7 +561,8 @@ func TestBuildDMLUpdateQuery(t *testing.T) {
{
sharedColumns := NewColumnList([]string{"id", "name", "position", "age"})
uniqueKeyColumns := NewColumnList([]string{"position", "name"})
query, sharedArgs, uniqueKeyArgs, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, uniqueKeyColumns, valueArgs, whereArgs)
generatedColumns := NewColumnList([]string{})
query, sharedArgs, uniqueKeyArgs, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, generatedColumns, uniqueKeyColumns, valueArgs, whereArgs)
test.S(t).ExpectNil(err)
expected := `
update /* gh-ost mydb.tbl */
Expand All @@ -576,7 +578,8 @@ func TestBuildDMLUpdateQuery(t *testing.T) {
{
sharedColumns := NewColumnList([]string{"id", "name", "position", "age"})
uniqueKeyColumns := NewColumnList([]string{"age"})
query, sharedArgs, uniqueKeyArgs, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, uniqueKeyColumns, valueArgs, whereArgs)
generatedColumns := NewColumnList([]string{})
query, sharedArgs, uniqueKeyArgs, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, generatedColumns, uniqueKeyColumns, valueArgs, whereArgs)
test.S(t).ExpectNil(err)
expected := `
update /* gh-ost mydb.tbl */
Expand All @@ -592,7 +595,8 @@ func TestBuildDMLUpdateQuery(t *testing.T) {
{
sharedColumns := NewColumnList([]string{"id", "name", "position", "age"})
uniqueKeyColumns := NewColumnList([]string{"age", "position", "id", "name"})
query, sharedArgs, uniqueKeyArgs, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, uniqueKeyColumns, valueArgs, whereArgs)
generatedColumns := NewColumnList([]string{})
query, sharedArgs, uniqueKeyArgs, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, generatedColumns, uniqueKeyColumns, valueArgs, whereArgs)
test.S(t).ExpectNil(err)
expected := `
update /* gh-ost mydb.tbl */
Expand All @@ -608,20 +612,23 @@ func TestBuildDMLUpdateQuery(t *testing.T) {
{
sharedColumns := NewColumnList([]string{"id", "name", "position", "age"})
uniqueKeyColumns := NewColumnList([]string{"age", "surprise"})
_, _, _, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, uniqueKeyColumns, valueArgs, whereArgs)
generatedColumns := NewColumnList([]string{})
_, _, _, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, generatedColumns, uniqueKeyColumns, valueArgs, whereArgs)
test.S(t).ExpectNotNil(err)
}
{
sharedColumns := NewColumnList([]string{"id", "name", "position", "age"})
uniqueKeyColumns := NewColumnList([]string{})
_, _, _, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, uniqueKeyColumns, valueArgs, whereArgs)
generatedColumns := NewColumnList([]string{})
_, _, _, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, generatedColumns, uniqueKeyColumns, valueArgs, whereArgs)
test.S(t).ExpectNotNil(err)
}
{
sharedColumns := NewColumnList([]string{"id", "name", "position", "age"})
mappedColumns := NewColumnList([]string{"id", "name", "role", "age"})
uniqueKeyColumns := NewColumnList([]string{"id"})
query, sharedArgs, uniqueKeyArgs, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, mappedColumns, uniqueKeyColumns, valueArgs, whereArgs)
generatedColumns := NewColumnList([]string{})
query, sharedArgs, uniqueKeyArgs, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, mappedColumns, generatedColumns, uniqueKeyColumns, valueArgs, whereArgs)
test.S(t).ExpectNil(err)
expected := `
update /* gh-ost mydb.tbl */
Expand All @@ -643,10 +650,11 @@ func TestBuildDMLUpdateQuerySignedUnsigned(t *testing.T) {
valueArgs := []interface{}{3, "testname", "newval", int8(-17), int8(-2)}
whereArgs := []interface{}{3, "testname", "findme", int8(-3), 56}
sharedColumns := NewColumnList([]string{"id", "name", "position", "age"})
generatedColumns := NewColumnList([]string{})
uniqueKeyColumns := NewColumnList([]string{"position"})
{
// test signed
query, sharedArgs, uniqueKeyArgs, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, uniqueKeyColumns, valueArgs, whereArgs)
query, sharedArgs, uniqueKeyArgs, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, generatedColumns, uniqueKeyColumns, valueArgs, whereArgs)
test.S(t).ExpectNil(err)
expected := `
update /* gh-ost mydb.tbl */
Expand All @@ -663,7 +671,7 @@ func TestBuildDMLUpdateQuerySignedUnsigned(t *testing.T) {
// test unsigned
sharedColumns.SetUnsigned("age")
uniqueKeyColumns.SetUnsigned("position")
query, sharedArgs, uniqueKeyArgs, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, uniqueKeyColumns, valueArgs, whereArgs)
query, sharedArgs, uniqueKeyArgs, err := BuildDMLUpdateQuery(databaseName, tableName, tableColumns, sharedColumns, sharedColumns, generatedColumns, uniqueKeyColumns, valueArgs, whereArgs)
test.S(t).ExpectNil(err)
expected := `
update /* gh-ost mydb.tbl */
Expand Down
13 changes: 13 additions & 0 deletions go/sql/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,19 @@ func (this *ColumnList) IsSubsetOf(other *ColumnList) bool {
return true
}

// ConcatenateColumnList concatenates two column lists. It does not check for duplicates.
func ConcatenateColumnList(columns1, columns2 *ColumnList) *ColumnList {
result := &ColumnList{}
for _, col := range columns1.Columns() {
result.columns = append(result.columns, col)
}
for _, col := range columns2.Columns() {
result.columns = append(result.columns, col)
}
result.Ordinals = NewColumnsMap(result.columns)
return result
}

func (this *ColumnList) Len() int {
return len(this.columns)
}
Expand Down
2 changes: 2 additions & 0 deletions localtests/generated-columns57-unique/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, 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}');
set @last_insert_id := last_insert_id();
update gh_ost_test set jsonobj=JSON_OBJECT('_id', 27, 'name', 'carrot') where id=@last_insert_id and idb=27;
end ;;