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

Integrated the expression_api for expression validation #14

Draft
wants to merge 1 commit into
base: support-check-constraint-frontend
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 10 additions & 1 deletion ui/src/app/components/workspace/workspace.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,16 @@ export class WorkspaceComponent implements OnInit, OnDestroy {
maxWidth: '500px',
})
} else {
this.router.navigate(['/prepare-migration'])
this.fetch.verifyExpression().subscribe((res:any)=>{
if(!res){
this.router.navigate(['/prepare-migration'])
}
else{
this.data.getSummary()
window.location.reload()
}

})
}
}
})
Expand Down
4 changes: 2 additions & 2 deletions ui/src/app/services/fetch/fetch.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,8 @@ export class FetchService {
return this.http.post(`${this.url}/restore/tables`, payload)
}

validateCheckConstraint() {
return this.http.get(`${this.url}/validateCheckConstraint`)
verifyExpression() {
return this.http.get(`${this.url}/verifyExpression`)
}

updateCheckConstraint(tableId: string, payload: ICheckConstraints[]): any {
Expand Down
113 changes: 113 additions & 0 deletions webv2/api/schema.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
Expand All @@ -13,6 +14,7 @@
"github.com/GoogleCloudPlatform/spanner-migration-tool/common/constants"
"github.com/GoogleCloudPlatform/spanner-migration-tool/common/utils"
"github.com/GoogleCloudPlatform/spanner-migration-tool/conversion"
"github.com/GoogleCloudPlatform/spanner-migration-tool/expressions_api"
"github.com/GoogleCloudPlatform/spanner-migration-tool/internal"
"github.com/GoogleCloudPlatform/spanner-migration-tool/internal/reports"
"github.com/GoogleCloudPlatform/spanner-migration-tool/profiles"
Expand Down Expand Up @@ -488,6 +490,117 @@

}

// findColId based on constraint condition it will return colId.
func findColId(colDefs map[string]ddl.ColumnDef, condition string) string {
for _, colDef := range colDefs {
if strings.Contains(condition, colDef.Name) {
return colDef.Id
}
}
return ""
}

// removeCheckConstraint this method will remove the constraint which has error
func removeCheckConstraint(checkConstraints []ddl.CheckConstraint, expId string) []ddl.CheckConstraint {

Check failure on line 504 in webv2/api/schema.go

View workflow job for this annotation

GitHub Actions / lint

undefined: ddl.CheckConstraint

Check failure on line 504 in webv2/api/schema.go

View workflow job for this annotation

GitHub Actions / coverage

undefined: ddl.CheckConstraint
var filteredConstraints []ddl.CheckConstraint

Check failure on line 505 in webv2/api/schema.go

View workflow job for this annotation

GitHub Actions / lint

undefined: ddl.CheckConstraint

Check failure on line 505 in webv2/api/schema.go

View workflow job for this annotation

GitHub Actions / coverage

undefined: ddl.CheckConstraint

for _, checkConstraint := range checkConstraints {
if checkConstraint.ExprId != expId {
filteredConstraints = append(filteredConstraints, checkConstraint)
}
}
return filteredConstraints
}

// VerifyExpression this function will use expression_api to validate check constraint expressions and add the relevant error
// to suggestion tab and remove the check constraint which has error
func VerifyExpression(w http.ResponseWriter, r *http.Request) {
sessionState := session.GetSessionState()
if sessionState.Conv == nil || sessionState.Driver == "" {
http.Error(w, fmt.Sprintf("Schema is not converted or Driver is not configured properly. Please retry converting the database to Spanner."), http.StatusNotFound)
return
}
sessionState.Conv.ConvLock.Lock()
defer sessionState.Conv.ConvLock.Unlock()

spschema := sessionState.Conv.SpSchema

hasErrorOccurred := false

expressionDetailList := []internal.ExpressionDetail{}
ctx := context.Background()

for _, sp := range spschema {
for _, cc := range sp.CheckConstraints {

Check failure on line 534 in webv2/api/schema.go

View workflow job for this annotation

GitHub Actions / lint

sp.CheckConstraints undefined (type ddl.CreateTable has no field or method CheckConstraints)

Check failure on line 534 in webv2/api/schema.go

View workflow job for this annotation

GitHub Actions / coverage

sp.CheckConstraints undefined (type ddl.CreateTable has no field or method CheckConstraints)

colId := findColId(sp.ColDefs, cc.Expr)
expressionDetail := internal.ExpressionDetail{
Expression: cc.Expr,
Type: "CHECK",
ReferenceElement: internal.ReferenceElement{Name: sp.Name},
ExpressionId: cc.ExprId,
Metadata: map[string]string{"tableId": sp.Id, "colId": colId, "checkConstraintName": cc.Name},
}
expressionDetailList = append(expressionDetailList, expressionDetail)
}
}

accessor, err := expressions_api.NewExpressionVerificationAccessorImpl(ctx, session.GetSessionState().SpannerProjectId, session.GetSessionState().SpannerInstanceID)
if err != nil {
http.Error(w, fmt.Sprintf("Failed to create ExpressionVerificationAccessorImpl: %v", err), http.StatusNotFound)
return
}
verifyExpressionsInput := internal.VerifyExpressionsInput{
Conv: sessionState.Conv,
Source: "mysql",
ExpressionDetailList: expressionDetailList,
}

result := accessor.VerifyExpressions(ctx, verifyExpressionsInput)
if result.ExpressionVerificationOutputList != nil {
for _, ev := range result.ExpressionVerificationOutputList {
if !ev.Result {
hasErrorOccurred = true
tableId := ev.ExpressionDetail.Metadata["tableId"]
colId := ev.ExpressionDetail.Metadata["colId"]

spschema := sessionState.Conv.SpSchema[tableId]
spschema.CheckConstraints = removeCheckConstraint(spschema.CheckConstraints, ev.ExpressionDetail.ExpressionId)

Check failure on line 568 in webv2/api/schema.go

View workflow job for this annotation

GitHub Actions / lint

spschema.CheckConstraints undefined (type ddl.CreateTable has no field or method CheckConstraints)

Check failure on line 568 in webv2/api/schema.go

View workflow job for this annotation

GitHub Actions / coverage

spschema.CheckConstraints undefined (type ddl.CreateTable has no field or method CheckConstraints)
sessionState.Conv.SpSchema[tableId] = spschema

err := ev.Err.Error()
var issueType internal.SchemaIssue

switch {
case strings.Contains(err, "No matching signature for operator"):
issueType = internal.TypeMismatch

Check failure on line 576 in webv2/api/schema.go

View workflow job for this annotation

GitHub Actions / lint

undefined: internal.TypeMismatch

Check failure on line 576 in webv2/api/schema.go

View workflow job for this annotation

GitHub Actions / coverage

undefined: internal.TypeMismatch
case strings.Contains(err, "Syntax error"):
issueType = internal.InvalidCondition

Check failure on line 578 in webv2/api/schema.go

View workflow job for this annotation

GitHub Actions / lint

undefined: internal.InvalidCondition

Check failure on line 578 in webv2/api/schema.go

View workflow job for this annotation

GitHub Actions / coverage

undefined: internal.InvalidCondition
case strings.Contains(err, "Unrecognized name"):
issueType = internal.ColumnNotFound

Check failure on line 580 in webv2/api/schema.go

View workflow job for this annotation

GitHub Actions / lint

undefined: internal.ColumnNotFound

Check failure on line 580 in webv2/api/schema.go

View workflow job for this annotation

GitHub Actions / coverage

undefined: internal.ColumnNotFound
default:
fmt.Println("Unhandled error:", err)
return
}

colIssue := sessionState.Conv.SchemaIssues[tableId].ColumnLevelIssues[colId]

if !utilities.IsSchemaIssuePresent(colIssue, issueType) {
colIssue = append(colIssue, issueType)
}
sessionState.Conv.SchemaIssues[tableId].ColumnLevelIssues[colId] = colIssue

}

}
}

session.UpdateSessionFile()

w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(hasErrorOccurred)
}

// renameForeignKeys checks the new names for spanner name validity, ensures the new names are already not used by existing tables
// secondary indexes or foreign key constraints. If above checks passed then foreignKey renaming reflected in the schema else appropriate
// error thrown.
Expand Down
4 changes: 3 additions & 1 deletion webv2/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func getRoutes() *mux.Router {
}

ctx := context.Background()
spanneraccessor, _:= spanneraccessor.NewSpannerAccessorClientImpl(ctx)
spanneraccessor, _ := spanneraccessor.NewSpannerAccessorClientImpl(ctx)
dsClient, _ := ds.NewDatastreamClientImpl(ctx)
storageclient, _ := storageclient.NewStorageClientImpl(ctx)
validateResourceImpl := conversion.NewValidateResourcesImpl(spanneraccessor, &datastream_accessor.DatastreamAccessorImpl{},
Expand Down Expand Up @@ -79,6 +79,8 @@ func getRoutes() *mux.Router {
router.HandleFunc("/setparent", api.SetParentTable).Methods("GET")
router.HandleFunc("/removeParent", api.RemoveParentTable).Methods("POST")

router.HandleFunc("/verifyExpression", api.VerifyExpression).Methods("GET")

// TODO:(searce) take constraint names themselves which are guaranteed to be unique for Spanner.
router.HandleFunc("/drop/secondaryindex", api.DropSecondaryIndex).Methods("POST")
router.HandleFunc("/restore/secondaryIndex", api.RestoreSecondaryIndex).Methods("POST")
Expand Down
Loading