Skip to content

Commit

Permalink
added the column level validation for check constraints
Browse files Browse the repository at this point in the history
  • Loading branch information
Vivek Yadav committed Nov 18, 2024
1 parent 775049c commit 0e64c3a
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 55 deletions.
120 changes: 70 additions & 50 deletions ui/src/app/components/workspace/workspace.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class WorkspaceComponent implements OnInit, OnDestroy {
converObj!: Subscription
ddlsumconvObj!: Subscription
ddlObj!: Subscription
rerenderObj!: Subscription;
rerenderObj!: Subscription
isLeftColumnCollapse: boolean = false
isRightColumnCollapse: boolean = true
isMiddleColumnCollapse: boolean = true
Expand All @@ -70,7 +70,7 @@ export class WorkspaceComponent implements OnInit, OnDestroy {
private sidenav: SidenavService,
private router: Router,
private clickEvent: ClickEventService,
private fetch: FetchService,
private fetch: FetchService
) {
this.currentObject = null
}
Expand All @@ -95,15 +95,15 @@ export class WorkspaceComponent implements OnInit, OnDestroy {
this.isMiddleColumnCollapse = !flag
})

if (this.data.autoGenMap){
if (this.data.autoGenMap) {
this.autoGenMapObj = this.data.autoGenMap.subscribe((autoGenMap) => {
this.autoGenMap = autoGenMap
})
}
if (this.data.treeUpdate){
if (this.data.treeUpdate) {
this.rerenderObj = this.data.treeUpdate.subscribe(() => {
this.reRenderObjectExplorerSpanner();
});
this.reRenderObjectExplorerSpanner()
})
}

this.convObj = this.data.conv.subscribe((data: IConv) => {
Expand Down Expand Up @@ -132,24 +132,18 @@ export class WorkspaceComponent implements OnInit, OnDestroy {
this.objectExplorerInitiallyRender = true
}


if (this.currentObject && this.currentObject.type === ObjectExplorerNodeType.Table) {

this.ccData = this.currentObject
? this.conversion.getCheckConstrainst(this.currentObject.id, data)
: []
? this.conversion.getCheckConstrainst(this.currentObject.id, data)
: []

this.fkData = this.currentObject
? this.conversion.getFkMapping(this.currentObject.id, data)
: []


this.tableData = this.currentObject
? this.conversion.getColumnMapping(this.currentObject.id, data)
: []



}
if (
this.currentObject &&
Expand All @@ -162,7 +156,7 @@ export class WorkspaceComponent implements OnInit, OnDestroy {
this.currentObject.id
)
}
this.dialect = (this.conv.SpDialect === "postgresql") ? "PostgreSQL" : "Google Standard SQL"
this.dialect = this.conv.SpDialect === 'postgresql' ? 'PostgreSQL' : 'Google Standard SQL'
})

this.converObj = this.data.conversionRate.subscribe((rates: any) => {
Expand Down Expand Up @@ -190,11 +184,11 @@ export class WorkspaceComponent implements OnInit, OnDestroy {
this.convObj.unsubscribe()
this.ddlObj.unsubscribe()
this.ddlsumconvObj.unsubscribe()
if (this.autoGenMapObj){
if (this.autoGenMapObj) {
this.autoGenMapObj.unsubscribe()
}
if (this.rerenderObj){
this.rerenderObj.unsubscribe();
if (this.rerenderObj) {
this.rerenderObj.unsubscribe()
}
}

Expand Down Expand Up @@ -228,22 +222,20 @@ export class WorkspaceComponent implements OnInit, OnDestroy {
this.srcTree = this.conversion.createTreeNodeForSource(this.conv, this.conversionRates)
}


changeCurrentObject(object: FlatNode) {
if (object.type === ObjectExplorerNodeType.Table) {
this.currentObject = object
this.tableData = this.conversion.getColumnMapping(this.currentObject.id, this.conv)
this.ccData = this.conversion.getCheckConstrainst(this.currentObject.id, this.conv)
this.ccData = this.conversion.getCheckConstrainst(this.currentObject.id, this.conv)
this.fkData = []
this.fkData = this.conversion.getFkMapping(this.currentObject.id, this.conv)
this.fkData = this.conversion.getFkMapping(this.currentObject.id, this.conv)
} else if (object.type === ObjectExplorerNodeType.Index) {
this.currentObject = object
this.indexData = this.conversion.getIndexMapping(object.parentId, this.conv, object.id)
} else if (object.type === ObjectExplorerNodeType.Sequence) {
this.currentObject = object
this.sequenceData = this.conversion.getSequenceMapping(object.id, this.conv)
}
else {
} else {
this.currentObject = null
}
}
Expand Down Expand Up @@ -283,7 +275,7 @@ export class WorkspaceComponent implements OnInit, OnDestroy {
let config: IDbConfig = JSON.parse(localStorage.getItem(StorageKeys.Config)!)
connectionDetail = config?.hostName + ' : ' + config?.port
} else {
connectionDetail = this.conv.DatabaseName
connectionDetail = this.conv.DatabaseName
}
let viewAssesmentData: IViewAssesmentData = {
srcDbType: this.srcDbName,
Expand All @@ -301,12 +293,15 @@ export class WorkspaceComponent implements OnInit, OnDestroy {
downloadSession(this.conv)
}

downloadArtifacts(){
downloadArtifacts() {
let zip = new JSZip()
let fileNameHeader = `${this.conv.DatabaseName}`
this.fetch.getDStructuredReport().subscribe({
next: (resStructured: IStructuredReport) => {
let resJson = JSON.stringify(resStructured).replace(/9223372036854776000/g, '9223372036854775807')
let resJson = JSON.stringify(resStructured).replace(
/9223372036854776000/g,
'9223372036854775807'
)
let fileName = fileNameHeader + '_migration_structuredReport.json'
// add structured report to zip file
zip.file(fileName, resJson)
Expand All @@ -318,62 +313,64 @@ export class WorkspaceComponent implements OnInit, OnDestroy {
next: (resDDL: string) => {
// add spanner DDL to zip file
zip.file(fileNameHeader + '_spannerDDL.txt', resDDL)
let resJsonSession = JSON.stringify(this.conv).replace(/9223372036854776000/g, '9223372036854775807')
let resJsonSession = JSON.stringify(this.conv).replace(
/9223372036854776000/g,
'9223372036854775807'
)
let sessionFileName = `${this.conv.SessionName}_${this.conv.DatabaseType}_${fileNameHeader}.json`
// add session to zip file
zip.file(sessionFileName, resJsonSession)
// Generate the zip file asynchronously
zip.generateAsync({ type: 'blob' })
.then((blob: Blob) => {
var a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = `${fileNameHeader}_artifacts`;
a.click();
zip.generateAsync({ type: 'blob' }).then((blob: Blob) => {
var a = document.createElement('a')
a.href = URL.createObjectURL(blob)
a.download = `${fileNameHeader}_artifacts`
a.click()
})
}
},
})
}
},
})
}
},
})
}

// downloads structured report of the migration in JSON format
downloadStructuredReport(){
downloadStructuredReport() {
var a = document.createElement('a')
this.fetch.getDStructuredReport().subscribe({
next: (res: IStructuredReport) => {
let resJson = JSON.stringify(res).replace(/9223372036854776000/g, '9223372036854775807')
a.href = 'data:text/json;charset=utf-8,' + encodeURIComponent(resJson)
a.download = `${this.conv.DatabaseName}_migration_structuredReport.json`
a.click()
}
},
})
}

// downloads text report of the migration in text format in more human readable form
downloadTextReport(){
downloadTextReport() {
var a = document.createElement('a')
this.fetch.getDTextReport().subscribe({
next: (res: string) => {
a.href = 'data:text;charset=utf-8,' + encodeURIComponent(res)
a.download = `${this.conv.DatabaseName}_migration_textReport.txt`
a.click()
}
},
})
}

// downloads text file of Spanner's DDL of the schema. However this is optimized for reading and includes comments, foreign keys
// and doesn't add backticks around table and column names. This is not strictly
// legal Cloud Spanner DDL (Cloud Spanner doesn't currently support comments).
downloadDDL(){
// legal Cloud Spanner DDL (Cloud Spanner doesn't currently support comments).
downloadDDL() {
var a = document.createElement('a')
this.fetch.getDSpannerDDL().subscribe({
next: (res: string) => {
a.href = 'data:text;charset=utf-8,' + encodeURIComponent(res)
a.download = `${this.conv.DatabaseName}_spannerDDL.txt`
a.click()
}
},
})
}

Expand Down Expand Up @@ -413,27 +410,50 @@ export class WorkspaceComponent implements OnInit, OnDestroy {
prepareMigration() {
this.fetch.getTableWithErrors().subscribe({
next: (res: ITableIdAndName[]) => {
if (res != null && res.length !=0)
{
let errMsg = 'Please fix the errors for the following tables to move ahead: '+ res.map(x => x.Name).join(', ')
if (res != null && res.length != 0) {
let errMsg =
'Please fix the errors for the following tables to move ahead: ' +
res.map((x) => x.Name).join(', ')
this.dialog.open(InfodialogComponent, {
data: { message: errMsg, type: 'error', title: 'Error in Spanner Draft' },
maxWidth: '500px',
})
} else if (this.isOfflineStatus) {
this.dialog.open(InfodialogComponent, {
data: { message: "Please configure spanner project id and instance id to proceed", type: 'error', title: 'Configure Spanner' },
data: {
message: 'Please configure spanner project id and instance id to proceed',
type: 'error',
title: 'Configure Spanner',
},
maxWidth: '500px',
})
} else if (Object.keys(this.conv.SpSchema).length == 0) {
this.dialog.open(InfodialogComponent, {
data: { message: "Please restore some table(s) to proceed with the migration", type: 'error', title: 'All tables skipped' },
data: {
message: 'Please restore some table(s) to proceed with the migration',
type: 'error',
title: 'All tables skipped',
},
maxWidth: '500px',
})
} else {
this.router.navigate(['/prepare-migration'])
this.fetch.validateCheckConstraint().subscribe((res: any) => {
if (res) {
this.router.navigate(['/prepare-migration'])
} else {
this.dialog.open(InfodialogComponent, {
data: {
message:
'Type Mismatch Error. Check the dependencies of type in check constraints tab',
type: 'warning',
title: 'Type Mismatch Error',
},
maxWidth: '500px',
})
}
})
}
}
},
})
}
spannerTab() {
Expand Down
3 changes: 1 addition & 2 deletions ui/src/app/services/conversion/conversion.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,7 @@ export class ConversionService {
]
}
getCheckConstrainst(tableId: string, data: IConv): ICcTabData[] {
debugger
let srcArr = data.SrcSchema[tableId].CheckConstraints
let srcArr = data.SrcSchema[tableId].CheckConstraints || []
let spArr = data.SpSchema[tableId].CheckConstraint || []

let res: ICcTabData[] = []
Expand Down
4 changes: 4 additions & 0 deletions ui/src/app/services/fetch/fetch.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@ export class FetchService {
)
}

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

updateTable(tableName: string, data: IUpdateTable): any {
return this.http.post<HttpResponse<IConv>>(`${this.url}/typemap/table?table=${tableName}`, data)
}
Expand Down
39 changes: 39 additions & 0 deletions webv2/api/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,45 @@ func UpdateCheckConstraint(w http.ResponseWriter, r *http.Request) {

}

// validate the type of column when there is check constraints
func ValidateCheckConstraint(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()

sp := sessionState.Conv.SpSchema
src := sessionState.Conv.SrcSchema

flag := true

for _, step1 := range src {

for _, step := range sp[step1.Id].ColDefs {

if len(sp[step1.Id].CheckConstraint) != 0 && len(src[step1.Id].CheckConstraints) != 0 {
spType := step.T.Name
srcType := src[step1.Id].ColDefs[step.Id].Type

actualType := mysqlDefaultTypeMap[srcType.Name]

if actualType.Name != spType {
flag = false
break
}
}

}

}

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

// 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
6 changes: 3 additions & 3 deletions webv2/config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"GCPProjectID": "vivekyadav-1731900261",
"SpannerProjectID": "vivekyadav-1731900261",
"SpannerInstanceID": "spanner-demo"
"GCPProjectID": "",
"SpannerProjectID": "",
"SpannerInstanceID": ""
}
1 change: 1 addition & 0 deletions webv2/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func getRoutes() *mux.Router {
router.HandleFunc("/spannerDefaultTypeMap", api.SpannerDefaultTypeMap).Methods("GET")
router.HandleFunc("/autoGenMap", api.GetAutoGenMap).Methods("GET")
router.HandleFunc("/getSequenceKind", api.GetSequenceKind).Methods("GET")
router.HandleFunc("/validateCheckConstraint", api.ValidateCheckConstraint).Methods("GET")

router.HandleFunc("/setparent", api.SetParentTable).Methods("GET")
router.HandleFunc("/removeParent", api.RemoveParentTable).Methods("POST")
Expand Down

0 comments on commit 0e64c3a

Please sign in to comment.