Skip to content

Commit 6d64560

Browse files
Missing bracket (#35)
* Fix for double brackets Fixes the issues related to brackets #24 * Run all those constraints Fixes the issue #31, This prevents from the program to fail when it has issue running constraint related queries. If there is a error executing a query to fix the constraint, throw the error on the screen and continue till the end. At the end the user can manually fix anything if there is any issue. * Bump the version number
1 parent cf811c5 commit 6d64560

File tree

5 files changed

+101
-33
lines changed

5 files changed

+101
-33
lines changed

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,11 @@ For more examples how to use the tool, please check out the [wiki](https://githu
130130

131131
# Known Issues
132132

133-
1. If you have a composite unique index where one column is part of foreign key column then there are chances the constraint creation would fail.
134-
2. Fixing CHECK constraints isn't supported due to complexity, so recreating check constraints would fail, use `custom` subcommand to control the data being inserted
135-
3. On Greenplum Database partition tables are not supported (due to check constraint issues defined above), so use the `custom` sub command to define the data to be inserted to the column with check constraints
136-
4. Custom data types are not supported, use `custom` sub command to control the data for that custom data types
133+
1. We do struggle when recreating constraints, even though we do try to fix the primary key , foreign key, unique key. So there is no guarantee that the tool will fix all the constraints and manual intervention is needed in some cases.
134+
2. If you have a composite unique index where one column is part of foreign key column then there are chances the constraint creation would fail.
135+
3. Fixing CHECK constraints isn't supported due to complexity, so recreating check constraints would fail, use `custom` subcommand to control the data being inserted
136+
4. On Greenplum Database partition tables are not supported (due to check constraint issues defined above), so use the `custom` sub command to define the data to be inserted to the column with check constraints
137+
5. Custom data types are not supported, use `custom` sub command to control the data for that custom data types
137138

138139
# Developers / Collaboration
139140

constraintsRestore.go

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ var (
99
ignoreErr = []string{
1010
"ERROR #42P16 multiple primary keys for table",
1111
"already exists"}
12+
maxLoop = 10
1213
)
1314

1415
// Get Foriegn key objects
@@ -49,32 +50,35 @@ func FixConstraints() {
4950
func fixPKey(pk constraint) {
5051
Debugf("Fixing the Primary / Unique Key for table %s", pk.table)
5152
totalViolators := 1
52-
53+
totalLoop := 0
54+
5355
// Extract the columns from the list that was collected during backup
54-
keys, err := ColExtractor(pk.column, `\(.*?\)`)
56+
keys, err := ColExtractor(pk.column, `\(([^\[\]]*)\)`)
5557
if err != nil {
5658
Fatalf("unable to determine PK violators key columns: %v", err)
5759
}
58-
cols := strings.Trim(keys, "()")
60+
cols := TrimPrefixNSuffix(RemoveEverySuffixAfterADelimiter(keys, " where "), "(", ")")
5961

60-
for totalViolators > 0 { // Loop till we get a 0 value (i.e 0 violation )
62+
for totalViolators > 0 && totalLoop <= maxLoop { // Loop till we get a 0 value (i.e 0 violation ) or max 10 loops
6163
// How many violations are we having, if zero then loop breaks
6264
totalViolators = getTotalPKViolator(pk.table, cols)
6365
if totalViolators > 0 { // Found violation, time to fix it
6466

6567
// If there are two or more columns forming a PK or UK
66-
// lets only fix column by column.
67-
totalColumns := strings.Split(cols, ",")
68+
pkColumns := strings.Split(cols, ",")
6869

6970
// Get data type associated with the data types
70-
dTypes := getDatatype(pk.table, totalColumns)
71+
dTypes := getDatatype(pk.table, pkColumns)
7172

7273
//Fix the primary constraints by picking the columns from the
7374
//array, i.e we update the column one by one.
7475
for _, v := range dTypes {
7576
fixPKViolator(pk.table, v.Colname, v.Dtype)
7677
}
7778
}
79+
// If there is still violation the function deleteViolatingPkOrUkConstraints takes
80+
// care of it
81+
totalLoop++
7882
}
7983
}
8084

@@ -109,6 +113,15 @@ func fixFKey(con constraint) {
109113
Debugf("Checking / Fixing FOREIGN KEY Violation table: %s, column: %s, reference: %s(%s)",
110114
fkeyObjects.Table, fkeyObjects.Column, fkeyObjects.Reftable, fkeyObjects.Refcolumn)
111115

116+
// If its a composite FK relations then pick only one and fix it
117+
// TODO: This is a bad logic, this will not quarantee a fix for the composite
118+
// TODO: key. Clean this out later when get a proper solution to overcome
119+
// TODO: the composite key
120+
col := strings.Split(fkeyObjects.Column, ",")
121+
refCol := strings.Split(fkeyObjects.Refcolumn, ",")
122+
fkeyObjects.Column = col[0]
123+
fkeyObjects.Refcolumn = refCol[0]
124+
112125
// Loop till we reach the the end of the loop
113126
for totalViolators > 0 {
114127

@@ -139,7 +152,7 @@ func getForeignKeyColumns(con constraint) *ForeignKey {
139152
if err != nil {
140153
Fatalf("Unable to extract foreign key column from fk clause: %v", err)
141154
}
142-
fkCol = strings.Trim(fkCol, "()")
155+
fkCol = TrimPrefixNSuffix(fkCol, "(", ")")
143156

144157
// Extract the reference column from the clause
145158
refCol, err := ColExtractor(refClause, `\(.*?\)`)
@@ -150,7 +163,7 @@ func getForeignKeyColumns(con constraint) *ForeignKey {
150163
// Extract reference table from the clause
151164
refTab := strings.Replace(refClause, refCol, "", -1)
152165
refTab = strings.Replace(refTab, "REFERENCES ", "", -1)
153-
refCol = strings.Trim(refCol, "()")
166+
refCol = TrimPrefixNSuffix(refCol, "(", ")")
154167

155168
return &ForeignKey{con.table, fkCol, refTab, refCol}
156169
}
@@ -187,7 +200,7 @@ func recreateAllConstraints() {
187200
}
188201

189202
// Start the progress bar
190-
bar := StartProgressBar(fmt.Sprintf("Recreated the Constraint Type \"%s\"", con), len(contents))
203+
bar := StartProgressBar(fmt.Sprintf("Recreating the constraint type \"%s\"", con), len(contents))
191204

192205
// Recreate all constraints one by one, if we can't create it then display the message
193206
// on the screen and continue with the rest, since we don't want it to fail if we cannot
@@ -198,7 +211,7 @@ func recreateAllConstraints() {
198211
Debugf("Error creating constraint %s, err: %v", content, err)
199212
// Try an attempt to recreate constraint again after deleting the
200213
// violating row
201-
successOrFailure := deleteViolatingPkOrUkConstriants(content)
214+
successOrFailure := deleteViolatingPkOrUkConstraints(content)
202215
if !successOrFailure { // didn't succeed, ask the user to fix it manually
203216
err = WriteToFile(failedConstraintsFile, content+"\n")
204217
if err != nil {
@@ -226,15 +239,13 @@ func recreateAllConstraints() {
226239
// is, we will delete the rows that violates it and hoping that it will help in
227240
// recreating the constraints. Yes we will loose that row at least that help to
228241
// recreate constraints ( fingers crossed :) )
229-
func deleteViolatingPkOrUkConstriants(con string) bool {
242+
func deleteViolatingPkOrUkConstraints(con string) bool {
230243
Debugf("Attempting to run the constraint command %s second time, after deleting violating rows", con)
231244
// does the DDL contain PK or UK keyword then do the following
232245
// rest send them back for user to fix it.
233-
if isSubStringAvailableOnString(con, "ADD CONSTRAINT.*PRIMARY KEY|ADD CONSTRAINT.*UNIQUE") {
234-
column, _ := ColExtractor(con, `\(.*?\)`)
235-
table, _ := ColExtractor(con, `ALTER TABLE(.*)ADD CONSTRAINT`)
236-
table = strings.Trim(strings.Trim(table, "ALTER TABLE"), "ADD CONSTRAINT")
237-
column = strings.Trim(column, "()")
246+
if isSubStringAvailableOnString(con, "ADD CONSTRAINT.*PRIMARY KEY|ADD CONSTRAINT.*UNIQUE|CREATE UNIQUE INDEX") {
247+
// Extract the table and column name
248+
table, column := ExtractTableNColumnName(con)
238249
err := deleteViolatingConstraintKeys(table, column)
239250
if err != nil { // we failed to delete the the constraint violation rows
240251
Debugf("Error when deleting rows from the constraint violation rows: %v", err)
@@ -250,3 +261,30 @@ func deleteViolatingPkOrUkConstriants(con string) bool {
250261
}
251262
return false
252263
}
264+
265+
// Extract the table name and the column from the sql command
266+
func ExtractTableNColumnName(s string) (string, string) {
267+
var isItAlterStatement bool = true
268+
var table string
269+
var column string
270+
271+
// Is this a create statement
272+
if strings.HasPrefix(s, "CREATE UNIQUE") { // like create unique index statement
273+
isItAlterStatement = false
274+
}
275+
276+
// Extract the column name
277+
column, _ = ColExtractor(s, `\(([^\[\]]*)\)`)
278+
column = TrimPrefixNSuffix(RemoveEverySuffixAfterADelimiter(column, " where "), "(", ")")
279+
280+
// Extract the table name
281+
switch isItAlterStatement {
282+
case true:
283+
table, _ = ColExtractor(s, `ALTER TABLE(.*)ADD CONSTRAINT`)
284+
table = TrimPrefixNSuffix(table, "ALTER TABLE", "ADD CONSTRAINT")
285+
case false:
286+
table, _ = ColExtractor(s, `ON(.*)USING`)
287+
table = TrimPrefixNSuffix(table, "ON", "USING")
288+
}
289+
return table, column
290+
}

helpers.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,23 @@ func ColExtractor(conkey, regExp string) (string, error) {
206206
return "", nil
207207
}
208208

209+
// Trim brackets at the start and at the end
210+
func TrimPrefixNSuffix(s, prefix, suffix string) string {
211+
return strings.TrimPrefix(strings.TrimSuffix(s, suffix) , prefix)
212+
}
213+
214+
// Remove everything after a delimiter
215+
func RemoveEverySuffixAfterADelimiter(s string, d string) string {
216+
// Protect from upper case and lower case bugs
217+
s = strings.ToLower(s)
218+
d = strings.ToLower(d)
219+
if strings.Contains(s, d) {
220+
spiltString := strings.Split(s, d)
221+
return spiltString[0]
222+
}
223+
return s
224+
}
225+
209226
// If given a datatype see if it has a bracket or not.
210227
func BracketsExists(dt string) bool {
211228
var rgx = regexp.MustCompile(`\(.*\)`)
@@ -263,4 +280,11 @@ func CharLen(dt string) (int, error) {
263280
returnValue = 1
264281
}
265282
return returnValue, nil
283+
}
284+
285+
// New line if its not a debug
286+
func addNewLine() {
287+
if !cmdOptions.Debug {
288+
fmt.Println()
289+
}
266290
}

mock.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77

88
var (
99
programName = "mock"
10-
programVersion = "v2.5"
10+
programVersion = "v2.6"
1111
ExecutionTimestamp = TimeNow()
1212
Path = fmt.Sprintf("%s/%s/%s", os.Getenv("HOME"), programName, ExecutionTimestamp)
1313
)

sql.go

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -412,8 +412,9 @@ func getTotalPKViolator(tab, cols string) int {
412412

413413
_, err := db.Query(pg.Scan(&total), query)
414414
if err != nil {
415+
addNewLine()
415416
Debugf("query: %s", query)
416-
Fatalf("Error when executing the query to extract pk violators: %v", err)
417+
Errorf("Error when executing the query to extract pk violators: %v", err)
417418
}
418419

419420
return total
@@ -436,8 +437,9 @@ func GetPKViolators(tab, cols string) []DBViolationRow {
436437
query := strings.Replace(getPKViolator(tab, cols), "SELECT "+cols, "SELECT "+cols+" AS row", -1)
437438
_, err := db.Query(&result, query)
438439
if err != nil {
440+
addNewLine()
439441
Debugf("query: %s", query)
440-
Fatalf("Error when executing the query to extract pk violators for table %s: %v", tab, err)
442+
Errorf("Error when executing the query to extract pk violators for table %s: %v", tab, err)
441443
}
442444

443445
return result
@@ -457,9 +459,9 @@ WHERE ctid =
457459
query = fmt.Sprintf(query, tab, col, newdata, whichrow)
458460
_, err := ExecuteDB(query)
459461
if err != nil {
460-
fmt.Println()
462+
addNewLine()
461463
Debugf("query: %s", query)
462-
Fatalf("Error when updating the primary key for table %s, err: %v", tab, err)
464+
Errorf("Error when updating the primary key for table %s, err: %v", tab, err)
463465
}
464466
return ""
465467
}
@@ -489,8 +491,9 @@ func GetTotalFKViolators(key ForeignKey) int {
489491

490492
_, err := db.Query(pg.Scan(&total), query)
491493
if err != nil {
494+
addNewLine()
492495
Debugf("Query: %s", query)
493-
Fatalf("Error when executing the query to total rows of foreign keys for table %s: %v", key.Table, err)
496+
Errorf("Error when executing the query to total rows of foreign keys for table %s: %v", key.Table, err)
494497
}
495498

496499
return total
@@ -507,9 +510,9 @@ func TotalRows(tab string) int {
507510

508511
_, err := db.Query(pg.Scan(&total), query)
509512
if err != nil {
510-
fmt.Println()
513+
addNewLine()
511514
Debugf("query: %s", query)
512-
Fatalf("Error when executing the query to total rows: %v", err)
515+
Errorf("Error when executing the query to total rows: %v", err)
513516
}
514517

515518
return total
@@ -527,8 +530,9 @@ func GetFKViolators(key ForeignKey) []DBViolationRow {
527530
query := strings.Replace(getFKViolators(key), "SELECT "+key.Column, "SELECT "+key.Column+" AS row", -1)
528531
_, err := db.Query(&result, query)
529532
if err != nil {
533+
addNewLine()
530534
Debugf("query: %s", query)
531-
Fatalf("Error when executing the query to extract fk violators for table %s: %v", key.Table, err)
535+
Errorf("Error when executing the query to extract fk violators for table %s: %v", key.Table, err)
532536
}
533537

534538
return result
@@ -547,15 +551,15 @@ WHERE %[2]s = '%[6]s'
547551
query = fmt.Sprintf(query, key.Table, key.Column, key.Refcolumn, key.Reftable, totalRows, whichRow)
548552
_, err := ExecuteDB(query)
549553
if err != nil {
550-
fmt.Println()
554+
addNewLine()
551555
Debugf("query: %s", query)
552-
Fatalf("Error when updating the foreign key for table %s, err: %v", key.Table, err)
556+
Errorf("Error when updating the foreign key for table %s, err: %v", key.Table, err)
553557
}
554558
}
555559

556560
// Delete the violating key
557561
func deleteViolatingConstraintKeys(tab string, column string) error {
558-
Debugf("Deleting the rows of the table that violate the constraints: %s:(%s)", tab, column)
562+
Debugf("Deleting the rows of the table that violate the constraints: %s(%s)", tab, column)
559563
query := `
560564
DELETE
561565
FROM %[1]s
@@ -570,6 +574,7 @@ WHERE (
570574
query = fmt.Sprintf(query, tab, column)
571575
_, err := ExecuteDB(query)
572576
if err != nil {
577+
Debug("query: %s", query)
573578
return err
574579
}
575580
return nil

0 commit comments

Comments
 (0)