@@ -9,40 +9,61 @@ import (
99 sqlite "github.com/gwenn/gosqlite"
1010)
1111
12+ // DiffType specifies the type of change in a row or object
1213type DiffType string
1314
1415const (
15- ACTION_ADD DiffType = "add"
16- ACTION_DELETE DiffType = "delete"
17- ACTION_MODIFY DiffType = "modify"
16+ // ActionAdd is used for inserted rows and created objects
17+ ActionAdd DiffType = "add"
18+
19+ // ActionDelete is used for deleted rows and dropped objects
20+ ActionDelete DiffType = "delete"
21+
22+ // ActionModify is used for updated rows and altered objects
23+ ActionModify DiffType = "modify"
1824)
1925
26+ // MergeStrategy specifies the type of SQL statements included in the diff results.
27+ // The SQL statements can be used for merging databases and depending on whether and
28+ // how you want to merge you should choose your merge strategy.
2029type MergeStrategy int
2130
2231const (
32+ // NoMerge removes any SQL statements for merging from the diff results
2333 NoMerge MergeStrategy = iota
34+
35+ // PreservePkMerge produces SQL statements which preserve the values of the primary key columns.
36+ // Executing these statements on the first database produces a database similar to the second.
2437 PreservePkMerge
38+
39+ // NewPkMerge produces SQL statements which generate new values for the primary key columns when
40+ // executed. This avoids a couple of possible conflicts and allows merging more distant databases.
2541 NewPkMerge
2642)
2743
44+ // SchemaDiff describes the changes to the schema of a database object, i.e. a created, dropped or altered object
2845type SchemaDiff struct {
2946 ActionType DiffType `json:"action_type"`
3047 Sql string `json:"sql"`
3148}
3249
50+ // DataDiff stores a single change in the data of a table, i.e. a single new, deleted, or changed row
3351type DataDiff struct {
3452 ActionType DiffType `json:"action_type"`
3553 Sql string `json:"sql"`
3654 Pk []DataValue `json:"pk"`
3755}
3856
57+ // DiffObjectChangeset stores all the differences between two objects in a database, for example two tables.
58+ // Both Schema and Data are optional and can be nil if there are no respective changes in this object.
3959type DiffObjectChangeset struct {
4060 ObjectName string `json:"object_name"`
4161 ObjectType string `json:"object_type"`
4262 Schema * SchemaDiff `json:"schema"`
4363 Data []DataDiff `json:"data"`
4464}
4565
66+ // Diffs is able to store all the differences between two databases.
4667type Diffs struct {
4768 Diff []DiffObjectChangeset `json:"diff"`
4869 // TODO Add PRAGMAs here
@@ -171,15 +192,15 @@ func diffSingleObject(sdb *sqlite.Conn, objectName string, objectType string, me
171192
172193 // Check for dropped object
173194 if sqlInMain != "" && sqlInAux == "" {
174- diff .Schema = & SchemaDiff {ActionType : ACTION_DELETE }
195+ diff .Schema = & SchemaDiff {ActionType : ActionDelete }
175196 if merge != NoMerge {
176197 diff .Schema .Sql = "DROP " + strings .ToUpper (objectType ) + " " + EscapeId (objectName ) + ";"
177198 }
178199
179200 // If this is a table, also add all the deleted data to the diff
180201 if objectType == "table" {
181202 // We never include the SQL statements because there is no need to delete all the rows when we DROP the table anyway
182- diff .Data , err = dataDiffForAllTableRows (sdb , "main" , objectName , ACTION_DELETE , false )
203+ diff .Data , err = dataDiffForAllTableRows (sdb , "main" , objectName , ActionDelete , false )
183204 if err != nil {
184205 return false , DiffObjectChangeset {}, err
185206 }
@@ -191,14 +212,14 @@ func diffSingleObject(sdb *sqlite.Conn, objectName string, objectType string, me
191212
192213 // Check for added object
193214 if sqlInMain == "" && sqlInAux != "" {
194- diff .Schema = & SchemaDiff {ActionType : ACTION_ADD }
215+ diff .Schema = & SchemaDiff {ActionType : ActionAdd }
195216 if merge != NoMerge {
196217 diff .Schema .Sql = sqlInAux + ";"
197218 }
198219
199220 // If this is a table, also add all the added data to the diff
200221 if objectType == "table" {
201- diff .Data , err = dataDiffForAllTableRows (sdb , "aux" , objectName , ACTION_ADD , merge != NoMerge )
222+ diff .Data , err = dataDiffForAllTableRows (sdb , "aux" , objectName , ActionAdd , merge != NoMerge )
202223 if err != nil {
203224 return false , DiffObjectChangeset {}, err
204225 }
@@ -210,7 +231,7 @@ func diffSingleObject(sdb *sqlite.Conn, objectName string, objectType string, me
210231
211232 // Check for modified object
212233 if sqlInMain != "" && sqlInAux != "" && sqlInMain != sqlInAux {
213- diff .Schema = & SchemaDiff {ActionType : ACTION_MODIFY }
234+ diff .Schema = & SchemaDiff {ActionType : ActionModify }
214235 if merge != NoMerge {
215236 diff .Schema .Sql = "DROP " + strings .ToUpper (objectType ) + " " + EscapeId (objectName ) + ";" + sqlInAux + ";"
216237 }
@@ -219,15 +240,15 @@ func diffSingleObject(sdb *sqlite.Conn, objectName string, objectType string, me
219240
220241 // If this is a table, also add all the data to the diff
221242 if objectType == "table" {
222- delete_data , err := dataDiffForAllTableRows (sdb , "main" , objectName , ACTION_DELETE , false )
243+ deleteData , err := dataDiffForAllTableRows (sdb , "main" , objectName , ActionDelete , false )
223244 if err != nil {
224245 return false , DiffObjectChangeset {}, err
225246 }
226- add_data , err := dataDiffForAllTableRows (sdb , "aux" , objectName , ACTION_ADD , merge != NoMerge )
247+ addData , err := dataDiffForAllTableRows (sdb , "aux" , objectName , ActionAdd , merge != NoMerge )
227248 if err != nil {
228249 return false , DiffObjectChangeset {}, err
229250 }
230- diff .Data = append (delete_data , add_data ... )
251+ diff .Data = append (deleteData , addData ... )
231252 }
232253
233254 // No further changes for modified objects. So we can return here
@@ -256,26 +277,21 @@ func diffSingleObject(sdb *sqlite.Conn, objectName string, objectType string, me
256277
257278func dataDiffForAllTableRows (sdb * sqlite.Conn , schemaName string , tableName string , action DiffType , includeSql bool ) (diff []DataDiff , err error ) {
258279 // Retrieve a list of all primary key columns and other columns in this table
259- pk , implicit_pk , other_columns , err := GetPrimaryKeyAndOtherColumns (sdb , schemaName , tableName )
280+ pk , implicitPk , otherColumns , err := GetPrimaryKeyAndOtherColumns (sdb , schemaName , tableName )
260281 if err != nil {
261282 return nil , err
262283 }
263284
264285 // Escape all the column names
265- var pk_escaped , other_escaped []string
266- for _ , v := range pk {
267- pk_escaped = append (pk_escaped , EscapeId (v ))
268- }
269- for _ , v := range other_columns {
270- other_escaped = append (other_escaped , EscapeId (v ))
271- }
286+ pkEscaped := EscapeIds (pk )
287+ otherEscaped := EscapeIds (otherColumns )
272288
273289 // Prepare query for the primary keys of all rows in this table. Only include the rest of the data
274290 // in the rows if required
275- query := "SELECT " + strings .Join (pk_escaped , "," )
276- if includeSql && action == ACTION_ADD {
277- if len (other_escaped ) > 0 {
278- query += "," + strings .Join (other_escaped , "," )
291+ query := "SELECT " + strings .Join (pkEscaped , "," )
292+ if includeSql && action == ActionAdd {
293+ if len (otherEscaped ) > 0 {
294+ query += "," + strings .Join (otherEscaped , "," )
279295 }
280296 }
281297 query += " FROM " + EscapeId (schemaName ) + "." + EscapeId (tableName )
@@ -292,17 +308,17 @@ func dataDiffForAllTableRows(sdb *sqlite.Conn, schemaName string, tableName stri
292308
293309 // Prepare SQL statement when needed
294310 if includeSql {
295- if action == ACTION_DELETE {
311+ if action == ActionDelete {
296312 d .Sql = "DELETE FROM " + EscapeId (tableName ) + " WHERE "
297- } else if action == ACTION_ADD {
298- var insert_columns []string
313+ } else if action == ActionAdd {
314+ var insertColumns []string
299315 // Don't include rowid column, only regular PK
300- if ! implicit_pk {
301- insert_columns = append (insert_columns , pk_escaped ... )
316+ if ! implicitPk {
317+ insertColumns = append (insertColumns , pkEscaped ... )
302318 }
303- insert_columns = append (insert_columns , other_escaped ... )
319+ insertColumns = append (insertColumns , otherEscaped ... )
304320
305- d .Sql = "INSERT INTO " + EscapeId (tableName ) + "(" + strings .Join (insert_columns , "," ) + ") VALUES("
321+ d .Sql = "INSERT INTO " + EscapeId (tableName ) + "(" + strings .Join (insertColumns , "," ) + ") VALUES("
306322 }
307323 }
308324
@@ -315,8 +331,8 @@ func dataDiffForAllTableRows(sdb *sqlite.Conn, schemaName string, tableName stri
315331
316332 // If we want to include a SQL statement for deleting data and this is still
317333 // part of the primary key, add this to the prepared DELETE statement
318- if includeSql && action == ACTION_DELETE && i < len (pk ) {
319- d .Sql += pk_escaped [i ]
334+ if includeSql && action == ActionDelete && i < len (pk ) {
335+ d .Sql += pkEscaped [i ]
320336 if row [i ].Type == Null {
321337 d .Sql += " IS NULL"
322338 } else {
@@ -327,17 +343,17 @@ func dataDiffForAllTableRows(sdb *sqlite.Conn, schemaName string, tableName stri
327343
328344 // If we want to include a SQL statement for adding data and this is the regular
329345 // data part, add this to the prepared INSERT statement
330- if includeSql && action == ACTION_ADD && i >= len (pk ) {
346+ if includeSql && action == ActionAdd && i >= len (pk ) {
331347 d .Sql += EscapeValue (row [i ]) + ","
332348 }
333349 }
334350
335351 // Remove the last " AND " of the SQL query for DELETE statements and the last "," for INSERT statements
336352 // and add a semicolon instead
337353 if includeSql {
338- if action == ACTION_DELETE {
354+ if action == ActionDelete {
339355 d .Sql = strings .TrimSuffix (d .Sql , " AND " ) + ";"
340- } else if action == ACTION_ADD {
356+ } else if action == ActionAdd {
341357 d .Sql = strings .TrimSuffix (d .Sql , "," ) + ");"
342358 }
343359 }
@@ -354,7 +370,7 @@ func dataDiffForAllTableRows(sdb *sqlite.Conn, schemaName string, tableName stri
354370// schemas match.
355371func dataDiffForModifiedTableRows (sdb * sqlite.Conn , tableName string , merge MergeStrategy ) (diff []DataDiff , err error ) {
356372 // Retrieve a list of all primary key columns and other columns in this table
357- pk , implicitPk , other_columns , err := GetPrimaryKeyAndOtherColumns (sdb , "aux" , tableName )
373+ pk , implicitPk , otherColumns , err := GetPrimaryKeyAndOtherColumns (sdb , "aux" , tableName )
358374 if err != nil {
359375 return nil , err
360376 }
@@ -371,7 +387,7 @@ func dataDiffForModifiedTableRows(sdb *sqlite.Conn, tableName string, merge Merg
371387
372388 // Escape all column names
373389 pkEscaped := EscapeIds (pk )
374- otherEscaped := EscapeIds (other_columns )
390+ otherEscaped := EscapeIds (otherColumns )
375391
376392 // Build query for getting differences. This is based on the query produced by the sqldiff utility for SQLite.
377393 // The resulting query returns n+1+m*2 number of rows where n is the number of columns in the primary key and
@@ -384,12 +400,12 @@ func dataDiffForModifiedTableRows(sdb *sqlite.Conn, tableName string, merge Merg
384400
385401 // Updated rows
386402 // There can only be updated rows in tables with more columns than the primary key columns
387- if len (other_columns ) > 0 {
403+ if len (otherColumns ) > 0 {
388404 query = "SELECT "
389405 for _ , c := range pkEscaped { // Primary key columns first
390406 query += "B." + c + ","
391407 }
392- query += "'" + string (ACTION_MODIFY ) + "'" // Updated row
408+ query += "'" + string (ActionModify ) + "'" // Updated row
393409 for _ , c := range otherEscaped { // Other columns last
394410 query += ",A." + c + " IS NOT B." + c + ",B." + c
395411 }
@@ -414,7 +430,7 @@ func dataDiffForModifiedTableRows(sdb *sqlite.Conn, tableName string, merge Merg
414430 for _ , c := range pkEscaped { // Primary key columns first. This needs to be from the first table for deleted rows
415431 query += "A." + c + ","
416432 }
417- query += "'" + string (ACTION_DELETE ) + "'" // Deleted row
433+ query += "'" + string (ActionDelete ) + "'" // Deleted row
418434 query += strings .Repeat (",NULL" , len (otherEscaped )* 2 ) // Just NULL for all the other columns. They don't matter for deleted rows
419435
420436 query += " FROM main." + EscapeId (tableName ) + " A WHERE "
@@ -430,7 +446,7 @@ func dataDiffForModifiedTableRows(sdb *sqlite.Conn, tableName string, merge Merg
430446 for _ , c := range pkEscaped { // Primary key columns first. This needs to be from the second table for inserted rows
431447 query += "B." + c + ","
432448 }
433- query += "'" + string (ACTION_ADD ) + "'" // Inserted row
449+ query += "'" + string (ActionAdd ) + "'" // Inserted row
434450 for _ , c := range otherEscaped { // Other columns last. Always set the modified flag for inserted rows
435451 query += ",1,B." + c
436452 }
@@ -473,11 +489,11 @@ func dataDiffForModifiedTableRows(sdb *sqlite.Conn, tableName string, merge Merg
473489
474490 // Produce the SQL statement for merging
475491 if merge != NoMerge {
476- if d .ActionType == ACTION_MODIFY || d .ActionType == ACTION_DELETE {
492+ if d .ActionType == ActionModify || d .ActionType == ActionDelete {
477493 // For updated and deleted rows the merge strategy doesn't matter
478494
479495 // The first part of the UPDATE and DELETE statements is different
480- if d .ActionType == ACTION_MODIFY {
496+ if d .ActionType == ActionModify {
481497 d .Sql = "UPDATE " + EscapeId (tableName ) + " SET "
482498
483499 // For figuring out which values to set, start with the first column after the diff type column.
@@ -511,7 +527,7 @@ func dataDiffForModifiedTableRows(sdb *sqlite.Conn, tableName string, merge Merg
511527 d .Sql += " AND "
512528 }
513529 d .Sql = strings .TrimSuffix (d .Sql , " AND " ) + ";"
514- } else if d .ActionType == ACTION_ADD {
530+ } else if d .ActionType == ActionAdd {
515531 // For inserted rows the merge strategy actually does matter. The PreservePkMerge strategy is simple:
516532 // We just include all columns, no matter whether primary key or not, in the INSERT statement as-is.
517533 // For tables which don't have a primary key the same applies even when using the NewPkMerge strategy.
@@ -650,7 +666,6 @@ func hasIncrementingIntPk(sdb *sqlite.Conn, schemaName string, tableName string)
650666 // a table with an incrementing primary key.
651667 if err != nil && err != io .EOF {
652668 return false , nil
653- } else {
654- return true , nil
655669 }
670+ return true , nil
656671}
0 commit comments