diff --git a/webv2/primarykey/hotspot.go b/webv2/primarykey/hotspot.go index 2ce04d93d5..af75ee862e 100644 --- a/webv2/primarykey/hotspot.go +++ b/webv2/primarykey/hotspot.go @@ -71,7 +71,7 @@ func hotspotAutoincrement(insert []ddl.IndexKey, spannerTable ddl.CreateTable) { for i := 0; i < len(insert); i++ { for _, c := range spannerTable.ColDefs { - if insert[i].ColId == c.Name { + if insert[i].ColId == c.Id { spannerColumnId := c.Id detecthotspotAutoincrement(spannerTable, spannerColumnId) } diff --git a/webv2/primarykey/hotspot_test.go b/webv2/primarykey/hotspot_test.go new file mode 100644 index 0000000000..924a4c70b5 --- /dev/null +++ b/webv2/primarykey/hotspot_test.go @@ -0,0 +1,116 @@ +package primarykey + +import ( + "reflect" + "testing" + + "github.com/cloudspannerecosystem/harbourbridge/internal" + "github.com/cloudspannerecosystem/harbourbridge/schema" + "github.com/cloudspannerecosystem/harbourbridge/spanner/ddl" + "github.com/cloudspannerecosystem/harbourbridge/webv2/session" +) + +func TestDetectHotspot(t *testing.T) { + input1 := []ddl.CreateTable{{ + Name: "TimeStamp_t1", + Id: "t1", + ColIds: []string{"c1", "c2", "c3"}, + ColDefs: map[string]ddl.ColumnDef{ + "c1": {Name: "film_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c1"}, + "c2": {Name: "actor_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c2"}, + "c3": {Name: "last_update", T: ddl.Type{Name: ddl.Timestamp, Len: ddl.MaxLength}, Id: "c3"}, + }, + PrimaryKeys: []ddl.IndexKey{{ColId: "c3", Order: 1, Desc: false}, {ColId: "c2", Order: 2, Desc: false}}, + }, + { + Name: "AutoIncrement_t2", + Id: "t1", + ColIds: []string{"c1"}, + ColDefs: map[string]ddl.ColumnDef{ + "c1": {Name: "film_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c1"}, + }, + PrimaryKeys: []ddl.IndexKey{{ColId: "c1", Order: 1, Desc: false}}, + }, + } + + sessionState := session.GetSessionState() + sourcetable := schema.Table{ + Name: "t1", + Schema: "s", + ColIds: []string{"c1"}, + ColDefs: map[string]schema.Column{ + "c1": {Name: "film_id", Type: schema.Type{Name: ddl.String}, Id: "c1", Ignored: schema.Ignored{AutoIncrement: true}}, + }, + PrimaryKeys: []schema.Key{{ColId: "c1", Order: 1, Desc: false}}, + } + + //sourcetable := sessionState.Conv.SrcSchema[] + sessionState.Conv = &internal.Conv{ + SpSchema: ddl.Schema{ + "t1": input1[1], + //"t2": input1[1], + }, + SrcSchema: map[string]schema.Table{ + "t1": sourcetable, + }, + SchemaIssues: map[string]map[string][]internal.SchemaIssue{ + "t1": {}, + //"t2": {}, + }, + } + + for i, input := range input1 { + if i != 1 { + continue + } + isHotSpot(input.PrimaryKeys, input) + expected := []internal.SchemaIssue{internal.HotspotAutoIncrement} + if !reflect.DeepEqual(sessionState.Conv.SchemaIssues[input.Id]["c1"], expected) { + t.Errorf("hotspotTimestamp failed, expected: %v, got: %v", expected, sessionState.Conv.SchemaIssues[input.Id][input.Id]) + } + + } + +} + +/* +func TestDetecthotspotAutoincrement(t *testing.T) { + // Define test input + spannerTable := ddl.CreateTable{ + Name: "film_actor", + ColIds: []string{"c1", "c2", "c3"}, + ColDefs: map[string]ddl.ColumnDef{ + "c1": {Name: "film_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c1"}, + "c2": {Name: "actor_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c2"}, + "c3": {Name: "last_update", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c3"}, + }, + PrimaryKeys: []ddl.IndexKey{{ColId: "c1", Order: 1, Desc: true}, {ColId: "c2", Order: 2, Desc: true}}, + Id: "t1", + } + + spannerColumnId := "c1" + + sessionState := session.GetSessionState() + sessionState.Conv.SrcSchema = map[string]ddl.CreateTable{ + "t1":{ + Name: "film_actor", + ColDefs: map[string]ddl.ColumnDef{ + "c1": {Name: "film_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c1", Ignored: ddl.Ignored{AutoIncrement: true}}, + "c2": {Name: "actor_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c2"}, + "c3": {Name: "last_update", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c3"}, + }, + Id: "t1", + }, + }, + + // Call the function being tested + detecthotspotAutoincrement(spannerTable, spannerColumnId) + + // Check the output for correctness + schemaIssue := sessionState.Conv.SchemaIssues[spannerTable.Id][spannerColumnId] + expectedSchemaIssue := []internal.SchemaIssue{internal.HotspotAutoIncrement} + if !reflect.DeepEqual(schemaIssue, expectedSchemaIssue) { + t.Errorf("detecthotspotAutoincrement(%v, %v) = %v, expected %v", spannerTable, spannerColumnId, schemaIssue, expectedSchemaIssue) + } +} +*/ diff --git a/webv2/primarykey/primarykey_service.go b/webv2/primarykey/primarykey_service.go index fe9b908eae..4bbab7c834 100644 --- a/webv2/primarykey/primarykey_service.go +++ b/webv2/primarykey/primarykey_service.go @@ -84,17 +84,17 @@ func RemoveInterleave(conv *internal.Conv, spannertable ddl.CreateTable) { var parentPkFirstColumn string for i := 0; i < len(spannertable.PrimaryKeys); i++ { if spannertable.PrimaryKeys[i].Order == 1 { - childPkFirstColumn = spannertable.PrimaryKeys[i].ColId + childPkFirstColumn = spannertable.ColDefs[spannertable.PrimaryKeys[i].ColId].Name } } for i := 0; i < len(conv.SpSchema[spannertable.ParentId].PrimaryKeys); i++ { if conv.SpSchema[spannertable.ParentId].PrimaryKeys[i].Order == 1 { - parentPkFirstColumn = conv.SpSchema[spannertable.ParentId].PrimaryKeys[i].ColId + parentPkFirstColumn = conv.SpSchema[spannertable.ParentId].ColDefs[conv.SpSchema[spannertable.ParentId].PrimaryKeys[i].ColId].Name } } if childPkFirstColumn != parentPkFirstColumn { spannertable.ParentId = "" - conv.SpSchema[spannertable.Name] = spannertable + conv.SpSchema[spannertable.Id] = spannertable } } } diff --git a/webv2/primarykey/primarykey_test.go b/webv2/primarykey/primarykey_test.go index 52d145b5a3..8f357cc364 100644 --- a/webv2/primarykey/primarykey_test.go +++ b/webv2/primarykey/primarykey_test.go @@ -254,7 +254,6 @@ func TestPrimarykey(t *testing.T) { sessionState := session.GetSessionState() c := &internal.Conv{ - SpSchema: map[string]ddl.CreateTable{ "t1": { Name: "film_actor", @@ -275,11 +274,11 @@ func TestPrimarykey(t *testing.T) { sessionState.Conv = c tc := []struct { - name string - input PrimaryKeyRequest - statusCode int - res PrimaryKeyResponse - expectedRes PrimaryKeyResponse + name string + input PrimaryKeyRequest + statusCode int + conv internal.Conv + expectedConv internal.Conv }{ { name: "Table Id Not found", @@ -296,6 +295,210 @@ func TestPrimarykey(t *testing.T) { Columns: []Column{}}, statusCode: http.StatusBadRequest, }, + { + name: "unmarshalling error", + input: PrimaryKeyRequest{ + TableId: "t1", + Columns: []Column{{Order: 1, Desc: true, ColumnId: "-12"}}, + }, + statusCode: http.StatusBadRequest, + }, + { + name: "Not valid column order", + input: PrimaryKeyRequest{ + TableId: "t1", + Columns: []Column{{ColumnId: "c1", Order: 2, Desc: true}, {ColumnId: "c2", Desc: false, Order: 2}}, + }, + statusCode: http.StatusBadRequest, + }, + { + name: "invalid columnid error", + input: PrimaryKeyRequest{ + TableId: "t1", + Columns: []Column{{ColumnId: "c4", Desc: false, Order: 1}}, + }, + statusCode: http.StatusBadRequest, + }, + } + + for _, tt := range tc { + inputBytes, err := json.Marshal(tt.input) + if err != nil { + t.Fatal(err) + } + buffer := bytes.NewBuffer(inputBytes) + + req, err := http.NewRequest("POST", "/primarykey", buffer) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Content-Type", "application/json") + rr := httptest.NewRecorder() + + handler := http.HandlerFunc(PrimaryKey) + handler.ServeHTTP(rr, req) + var conv internal.Conv + json.Unmarshal(rr.Body.Bytes(), &conv) + assert.Equal(t, tt.statusCode, rr.Code) + if tt.statusCode == http.StatusOK { + assert.Equal(t, tt.expectedConv, conv) + } + } +} + +func TestPrimarykeyUpdate(t *testing.T) { + tc := []struct { + name string + input PrimaryKeyRequest + statusCode int + conv internal.Conv + expectedConv internal.Conv + }{ + { + name: "Test removed synthpk", + input: PrimaryKeyRequest{ + TableId: "t1", + Columns: []Column{{ColumnId: "c1", Desc: true, Order: 1}}, + }, + statusCode: http.StatusOK, + conv: internal.Conv{ + + SpSchema: map[string]ddl.CreateTable{ + "t1": { + Name: "film_actor", + ColIds: []string{"c1", "c2", "c3"}, + ColDefs: map[string]ddl.ColumnDef{ + "c1": {Name: "film_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c1"}, + "c2": {Name: "actor_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c2"}, + "c3": {Name: "synth_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c3"}, + }, + PrimaryKeys: []ddl.IndexKey{{ColId: "c3", Order: 1, Desc: true}, {ColId: "c1", Order: 2, Desc: true}}, + Id: "t1", + }}, + SyntheticPKeys: map[string]internal.SyntheticPKey{ + "t1": {ColId: "c3", Sequence: 0}, + }, + Audit: internal.Audit{ + MigrationType: migration.MigrationData_MIGRATION_TYPE_UNSPECIFIED.Enum(), + }, + }, + expectedConv: internal.Conv{ + SpSchema: map[string]ddl.CreateTable{ + "t1": { + Name: "film_actor", + ColIds: []string{"c1", "c2"}, + ColDefs: map[string]ddl.ColumnDef{ + "c1": {Name: "film_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c1"}, + "c2": {Name: "actor_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c2"}, + }, + PrimaryKeys: []ddl.IndexKey{{ColId: "c1", Order: 1, Desc: true}}, + Id: "t1", + }}, + SyntheticPKeys: map[string]internal.SyntheticPKey{}, + }, + }, + } + + for _, tt := range tc { + inputBytes, err := json.Marshal(tt.input) + if err != nil { + t.Fatal(err) + } + buffer := bytes.NewBuffer(inputBytes) + sessionState := session.GetSessionState() + + sessionState.Conv = &tt.conv + req, err := http.NewRequest("POST", "/primarykey", buffer) + if err != nil { + t.Fatal(err) + } + + req.Header.Set("Content-Type", "application/json") + rr := httptest.NewRecorder() + + handler := http.HandlerFunc(PrimaryKey) + handler.ServeHTTP(rr, req) + assert.Equal(t, tt.statusCode, rr.Code) + if tt.statusCode == http.StatusOK { + var res internal.Conv + json.Unmarshal(rr.Body.Bytes(), &res) + assert.Equal(t, tt.expectedConv, res) + } + } +} + +func TestPrimaryKeyUpdatetwo(t *testing.T) { + + tc := []struct { + name string + input PrimaryKeyRequest + statusCode int + conv internal.Conv + expectedConv internal.Conv + }{ + { + name: "remove interleave function success", + input: PrimaryKeyRequest{ + TableId: "t1", + Columns: []Column{{ColumnId: "c2", Desc: true, Order: 1}}, + }, + statusCode: http.StatusOK, + conv: internal.Conv{ + SpSchema: map[string]ddl.CreateTable{ + "t1": { + Name: "parent_table", + ColIds: []string{"c1", "c2", "c3"}, + ColDefs: map[string]ddl.ColumnDef{ + "c1": {Name: "film_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c1"}, + "c2": {Name: "actor_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c2"}, + "c3": {Name: "last_update", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c3"}, + }, + PrimaryKeys: []ddl.IndexKey{{ColId: "c1", Order: 1, Desc: true}, {ColId: "c2", Order: 2, Desc: true}}, + Id: "t1", + ParentId: "t2", + }, + "t2": { + Name: "child_table", + ColIds: []string{"c1", "c2"}, + ColDefs: map[string]ddl.ColumnDef{ + "c1": {Name: "film_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c1"}, + "c2": {Name: "film_details", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c2"}, + }, + PrimaryKeys: []ddl.IndexKey{{ColId: "c1", Order: 1, Desc: true}}, + Id: "t2", + }, + }, + Audit: internal.Audit{ + MigrationType: migration.MigrationData_MIGRATION_TYPE_UNSPECIFIED.Enum(), + }, + }, + expectedConv: internal.Conv{ + SpSchema: map[string]ddl.CreateTable{ + "t1": { + Name: "parent_table", + ColIds: []string{"c1", "c2", "c3"}, + ColDefs: map[string]ddl.ColumnDef{ + "c1": {Name: "film_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c1"}, + "c2": {Name: "actor_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c2"}, + "c3": {Name: "last_update", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c3"}, + }, + PrimaryKeys: []ddl.IndexKey{{ColId: "c2", Order: 1, Desc: true}}, + Id: "t1", + }, + "t2": { + Name: "child_table", + ColIds: []string{"c1", "c2"}, + ColDefs: map[string]ddl.ColumnDef{ + "c1": {Name: "film_id", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c1"}, + "c2": {Name: "film_details", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, Id: "c2"}, + }, + PrimaryKeys: []ddl.IndexKey{{ColId: "c1", Order: 1, Desc: true}}, + Id: "t2", + }, + }, + }, + }, } for _, tt := range tc { @@ -306,6 +509,10 @@ func TestPrimarykey(t *testing.T) { } buffer := bytes.NewBuffer(inputBytes) + sessionState := session.GetSessionState() + + sessionState.Conv = &tt.conv + req, err := http.NewRequest("POST", "/primarykey", buffer) if err != nil { t.Fatal(err) @@ -317,7 +524,11 @@ func TestPrimarykey(t *testing.T) { handler := http.HandlerFunc(PrimaryKey) handler.ServeHTTP(rr, req) - json.Unmarshal(rr.Body.Bytes(), &tt.res) assert.Equal(t, tt.statusCode, rr.Code) + if tt.statusCode == http.StatusOK { + var res internal.Conv + json.Unmarshal(rr.Body.Bytes(), &res) + assert.Equal(t, tt.expectedConv, res) + } } }