Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 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
f25eea1
Merge pull request #26 from openark/pk-change-to-json
shlomi-noach Jul 12, 2021
06fd4d5
Merge pull request #28 from github/master
shlomi-noach Jul 19, 2021
5fe0cb3
Trigger support - Not working
eldadts Jul 22, 2021
d291bf6
Trigger support -Working - No rollback
eldadts Aug 19, 2021
c112359
Support zero date and zero in date, via dedicated command line flag
shlomi-noach Oct 21, 2021
d379bb8
test that expects failure
shlomi-noach Oct 21, 2021
9a04696
support --allow-zero-in-date command line flag
shlomi-noach Oct 21, 2021
4cc40c9
5.5 behaves in a different way. I don't care. May as well remove supp…
shlomi-noach Oct 21, 2021
8c4f832
also 5.6 behaves in a special way?
shlomi-noach Oct 21, 2021
ad7aeb2
set sql_mode before ALTERing ghost table
shlomi-noach Oct 21, 2021
addd7bf
Merge pull request #31 from openark/zero-date
shlomi-noach Oct 21, 2021
e3ce501
support tables with zero date when --allow-zero-in-date provided
shlomi-noach Oct 26, 2021
dfd58d0
support go1.7
shlomi-noach Oct 26, 2021
22c2e3e
correct error message
shlomi-noach Oct 26, 2021
37813ec
support go1.7
shlomi-noach Oct 26, 2021
5e0f152
added test suite
shlomi-noach Oct 26, 2021
0ea04fb
relax sql_mode for test
shlomi-noach Oct 26, 2021
ded0f91
Merge pull request #32 from openark/existing-date-with-zero
shlomi-noach Oct 26, 2021
c91d9d7
add trigger struct, resolve comments in pr
eldadts Nov 14, 2021
0a271d0
go fmt
eldadts Nov 15, 2021
e049a6c
Merge pull request #1 from xtrimf/support_for_triggers
eldadts Nov 15, 2021
0f97a08
go fmt
eldadts Dec 1, 2021
1e4bbcb
reuse GetGhostTriggerName
eldadts Dec 1, 2021
d30bc0d
Merge pull request #2 from xtrimf/support_for_triggers
eldadts Dec 1, 2021
0138458
change trigger suffix logic
eldadts Dec 16, 2021
96cd286
Merge pull request #3 from xtrimf/support_for_triggers
eldadts Dec 16, 2021
765a1df
gofmt
shlomi-noach Jan 10, 2022
277dfa3
Merge pull request #30 from xtrimf/support_for_triggers_no_rollback
shlomi-noach Jan 10, 2022
e7d9342
merged master, resolved conflict
shlomi-noach Aug 21, 2022
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
24 changes: 24 additions & 0 deletions go/base/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"sync"
"sync/atomic"
"time"
"unicode/utf8"

uuid "github.com/satori/go.uuid"

Expand Down Expand Up @@ -219,6 +220,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 All @@ -229,6 +231,11 @@ type MigrationContext struct {
MigrationIterationRangeMaxValues *sql.ColumnValues
ForceTmpTableName string

IncludeTriggers bool
RemoveTriggerSuffix bool
TriggerSuffix string
Triggers []mysql.Trigger

recentBinlogCoordinates mysql.BinlogCoordinates

Log Logger
Expand Down Expand Up @@ -888,3 +895,20 @@ func (this *MigrationContext) ReadConfigFile() error {

return nil
}

// getGhostTriggerName generates the name of a ghost trigger, based on original trigger name
// or a given trigger name
func (this *MigrationContext) GetGhostTriggerName(triggerName string) string {
if this.RemoveTriggerSuffix && strings.HasSuffix(triggerName, this.TriggerSuffix) {
return strings.TrimSuffix(triggerName, this.TriggerSuffix)
}
// else
return triggerName + this.TriggerSuffix
}

// validateGhostTriggerLength check if the ghost trigger name length is not more than 64 characters
func (this *MigrationContext) ValidateGhostTriggerLengthBelowMaxLength(triggerName string) bool {
ghostTriggerName := this.GetGhostTriggerName(triggerName)

return utf8.RuneCountInString(ghostTriggerName) <= mysql.MaxTableNameLength
}
63 changes: 63 additions & 0 deletions go/base/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package base
import (
"io/ioutil"
"os"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -59,6 +60,68 @@ func TestGetTableNames(t *testing.T) {
}
}

func TestGetTriggerNames(t *testing.T) {
{
context := NewMigrationContext()
context.TriggerSuffix = "_gho"
test.S(t).ExpectEquals(context.GetGhostTriggerName("my_trigger"), "my_trigger"+context.TriggerSuffix)
}
{
context := NewMigrationContext()
context.TriggerSuffix = "_gho"
context.RemoveTriggerSuffix = true
test.S(t).ExpectEquals(context.GetGhostTriggerName("my_trigger"), "my_trigger"+context.TriggerSuffix)
}
{
context := NewMigrationContext()
context.TriggerSuffix = "_gho"
context.RemoveTriggerSuffix = true
test.S(t).ExpectEquals(context.GetGhostTriggerName("my_trigger_gho"), "my_trigger")
}
{
context := NewMigrationContext()
context.TriggerSuffix = "_gho"
context.RemoveTriggerSuffix = false
test.S(t).ExpectEquals(context.GetGhostTriggerName("my_trigger_gho"), "my_trigger_gho_gho")
}

}
func TestValidateGhostTriggerLengthBelowMaxLength(t *testing.T) {
{
context := NewMigrationContext()
context.TriggerSuffix = "_gho"
test.S(t).ExpectEquals(context.ValidateGhostTriggerLengthBelowMaxLength("my_trigger"), true)
}
{
context := NewMigrationContext()
context.TriggerSuffix = "_ghost"
test.S(t).ExpectEquals(context.ValidateGhostTriggerLengthBelowMaxLength(strings.Repeat("my_trigger_ghost", 4)), false) // 64 characters + "_ghost"
}
{
context := NewMigrationContext()
context.TriggerSuffix = "_ghost"
test.S(t).ExpectEquals(context.ValidateGhostTriggerLengthBelowMaxLength(strings.Repeat("my_trigger_ghost", 3)), true) // 48 characters + "_ghost"
}
{
context := NewMigrationContext()
context.TriggerSuffix = "_ghost"
context.RemoveTriggerSuffix = true
test.S(t).ExpectEquals(context.ValidateGhostTriggerLengthBelowMaxLength(strings.Repeat("my_trigger_ghost", 4)), true) // 64 characters + "_ghost" removed
}
{
context := NewMigrationContext()
context.TriggerSuffix = "_ghost"
context.RemoveTriggerSuffix = true
test.S(t).ExpectEquals(context.ValidateGhostTriggerLengthBelowMaxLength(strings.Repeat("my_trigger_ghost", 4)+"X"), false) // 65 characters + "_ghost" not removed
}
{
context := NewMigrationContext()
context.TriggerSuffix = "_ghost"
context.RemoveTriggerSuffix = true
test.S(t).ExpectEquals(context.ValidateGhostTriggerLengthBelowMaxLength(strings.Repeat("my_trigger_ghost", 4)+"_ghost"), true) // 70 characters + last "_ghost" removed
}
}

func TestReadConfigFile(t *testing.T) {
{
context := NewMigrationContext()
Expand Down
18 changes: 18 additions & 0 deletions go/cmd/gh-ost/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"net/url"
"os"
"os/signal"
"regexp"
"syscall"

"github.com/github/gh-ost/go/base"
Expand Down Expand Up @@ -132,6 +133,10 @@ func main() {

flag.UintVar(&migrationContext.ReplicaServerId, "replica-server-id", 99999, "server id used by gh-ost process. Default: 99999")

flag.BoolVar(&migrationContext.IncludeTriggers, "include-triggers", false, "When true, the triggers (if exist) will be created on the new table")
flag.StringVar(&migrationContext.TriggerSuffix, "trigger-suffix", "", "Add a suffix to the trigger name (i.e '_v2'). Requires '--include-triggers'")
flag.BoolVar(&migrationContext.RemoveTriggerSuffix, "remove-trigger-suffix-if-exists", false, "Remove given suffix from name of trigger. Requires '--include-triggers' and '--trigger-suffix'")

maxLoad := flag.String("max-load", "", "Comma delimited status-name=threshold. e.g: 'Threads_running=100,Threads_connected=500'. When status exceeds threshold, app throttles writes")
criticalLoad := flag.String("critical-load", "", "Comma delimited status-name=threshold, same format as --max-load. When status exceeds threshold, app panics and quits")
flag.Int64Var(&migrationContext.CriticalLoadIntervalMilliseconds, "critical-load-interval-millis", 0, "When 0, migration immediately bails out upon meeting critical-load. When non-zero, a second check is done after given interval, and migration only bails out if 2nd check still meets critical load")
Expand Down Expand Up @@ -245,6 +250,19 @@ func main() {
if *replicationLagQuery != "" {
migrationContext.Log.Warning("--replication-lag-query is deprecated")
}
if migrationContext.IncludeTriggers && migrationContext.TriggerSuffix == "" {
migrationContext.Log.Fatalf("--trigger-suffix must be used with --include-triggers")
}
if !migrationContext.IncludeTriggers && migrationContext.TriggerSuffix != "" {
migrationContext.Log.Fatalf("--trigger-suffix cannot be be used without --include-triggers")
}
if migrationContext.TriggerSuffix != "" {
regex := regexp.MustCompile(`^[\da-zA-Z_]+$`)

if !regex.Match([]byte(migrationContext.TriggerSuffix)) {
migrationContext.Log.Fatalf("--trigger-suffix must contain only alpha numeric characters and underscore (0-9,a-z,A-Z,_)")
}
}

switch *cutOver {
case "atomic", "default", "":
Expand Down
52 changes: 51 additions & 1 deletion go/logic/applier.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,56 @@ func (this *Applier) dropTable(tableName string) error {
return nil
}

// dropTriggers drop the triggers on the applied host
func (this *Applier) DropTriggersFromGhost() error {
if len(this.migrationContext.Triggers) > 0 {
for _, trigger := range this.migrationContext.Triggers {
triggerName := this.migrationContext.GetGhostTriggerName(trigger.Name)
query := fmt.Sprintf("drop trigger if exists %s", sql.EscapeName(triggerName))
_, err := sqlutils.ExecNoPrepare(this.db, query)
if err != nil {
return err
}
this.migrationContext.Log.Infof("Trigger '%s' dropped", triggerName)
}
}
return nil
}

// createTriggers creates the triggers on the applied host
func (this *Applier) createTriggers(tableName string) error {
if len(this.migrationContext.Triggers) > 0 {
for _, trigger := range this.migrationContext.Triggers {
triggerName := this.migrationContext.GetGhostTriggerName(trigger.Name)
query := fmt.Sprintf(`create /* gh-ost */ trigger %s %s %s on %s.%s for each row
%s`,
sql.EscapeName(triggerName),
trigger.Timing,
trigger.Event,
sql.EscapeName(this.migrationContext.DatabaseName),
sql.EscapeName(tableName),
trigger.Statement,
)
this.migrationContext.Log.Infof("Createing trigger %s on %s.%s",
sql.EscapeName(triggerName),
sql.EscapeName(this.migrationContext.DatabaseName),
sql.EscapeName(tableName),
)
if _, err := sqlutils.ExecNoPrepare(this.db, query); err != nil {
return err
}
}
this.migrationContext.Log.Infof("Triggers created on %s", tableName)
}
return nil
}

// CreateTriggers creates the original triggers on applier host
func (this *Applier) CreateTriggersOnGhost() error {
err := this.createTriggers(this.migrationContext.GetGhostTableName())
return err
}

// DropChangelogTable drops the changelog table on the applier host
func (this *Applier) DropChangelogTable() error {
return this.dropTable(this.migrationContext.GetChangelogTableName())
Expand Down Expand Up @@ -1089,7 +1139,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
96 changes: 91 additions & 5 deletions go/logic/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,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 @@ -483,7 +483,7 @@ func (this *Inspector) validateTableForeignKeys(allowChildForeignKeys bool) erro
return nil
}

// validateTableTriggers makes sure no triggers exist on the migrated table
// validateTableTriggers makes sure no triggers exist on the migrated table. if --include_triggers is used then it fetches the triggers
func (this *Inspector) validateTableTriggers() error {
query := `
SELECT COUNT(*) AS num_triggers
Expand All @@ -505,12 +505,74 @@ func (this *Inspector) validateTableTriggers() error {
return err
}
if numTriggers > 0 {
return this.migrationContext.Log.Errorf("Found triggers on %s.%s. Triggers are not supported at this time. Bailing out", sql.EscapeName(this.migrationContext.DatabaseName), sql.EscapeName(this.migrationContext.OriginalTableName))
if this.migrationContext.IncludeTriggers {
this.migrationContext.Log.Infof("Found %d triggers on %s.%s.", numTriggers, sql.EscapeName(this.migrationContext.DatabaseName), sql.EscapeName(this.migrationContext.OriginalTableName))
this.migrationContext.Triggers, err = mysql.GetTriggers(this.db, this.migrationContext.DatabaseName, this.migrationContext.OriginalTableName)
if err != nil {
return err
}
if err := this.validateGhostTriggersDontExist(); err != nil {
return err
}
if err := this.validateGhostTriggersLength(); err != nil {
return err
}
return nil
}
return this.migrationContext.Log.Errorf("Found triggers on %s.%s. Tables with triggers are supported only when using \"include-triggers\" flag. Bailing out", sql.EscapeName(this.migrationContext.DatabaseName), sql.EscapeName(this.migrationContext.OriginalTableName))

}
this.migrationContext.Log.Debugf("Validated no triggers exist on table")
return nil
}

// verifyTriggersDontExist verifies before createing new triggers we want to make sure these triggers dont exist already in the DB
func (this *Inspector) validateGhostTriggersDontExist() error {
if len(this.migrationContext.Triggers) > 0 {
var foundTriggers []string
for _, trigger := range this.migrationContext.Triggers {
triggerName := this.migrationContext.GetGhostTriggerName(trigger.Name)
query := "select 1 from information_schema.triggers where trigger_name = ? and trigger_schema = ?"
err := sqlutils.QueryRowsMap(this.db, query, func(rowMap sqlutils.RowMap) error {
triggerExists := rowMap.GetInt("1")
if triggerExists == 1 {
foundTriggers = append(foundTriggers, triggerName)
}
return nil
},
triggerName,
this.migrationContext.DatabaseName,
)
if err != nil {
return err
}
}
if len(foundTriggers) > 0 {
return this.migrationContext.Log.Errorf("Found gh-ost triggers (%s). Please use a different suffix or drop them. Bailing out", strings.Join(foundTriggers, ","))
}
}

return nil
}

func (this *Inspector) validateGhostTriggersLength() error {
if len(this.migrationContext.Triggers) > 0 {
var foundTriggers []string
for _, trigger := range this.migrationContext.Triggers {
triggerName := this.migrationContext.GetGhostTriggerName(trigger.Name)
if ok := this.migrationContext.ValidateGhostTriggerLengthBelowMaxLength(triggerName); !ok {
foundTriggers = append(foundTriggers, triggerName)
}

}
if len(foundTriggers) > 0 {
return this.migrationContext.Log.Errorf("Gh-ost triggers (%s) length > %d characters. Bailing out", strings.Join(foundTriggers, ","), mysql.MaxTableNameLength)
}
}

return nil
}

// estimateTableRowsViaExplain estimates number of rows on original table
func (this *Inspector) estimateTableRowsViaExplain() error {
query := fmt.Sprintf(`explain select /* gh-ost */ * from %s.%s where 1=1`, sql.EscapeName(this.migrationContext.DatabaseName), sql.EscapeName(this.migrationContext.OriginalTableName))
Expand Down Expand Up @@ -742,8 +804,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 @@ -776,6 +839,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 @@ -784,7 +870,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
Loading