@@ -22,14 +22,22 @@ type SchemaDiff struct {
22
22
Sql string `json:"sql"`
23
23
}
24
24
25
+ type DataDiff struct {
26
+ ActionType DiffType `json:"action_type"`
27
+ Sql string `json:"sql"`
28
+ Pk []DataValue `json:"pk"`
29
+ }
30
+
25
31
type DiffObjectChangeset struct {
26
32
ObjectName string `json:"object_name"`
27
33
ObjectType string `json:"object_type"`
28
34
Schema SchemaDiff `json:"schema"`
35
+ Data []DataDiff `json:"data"`
29
36
}
30
37
31
38
type Diffs struct {
32
39
Diff []DiffObjectChangeset `json:"diff"`
40
+ // TODO Add PRAGMAs here
33
41
}
34
42
35
43
// Diff generates the differences between the two commits commitA and commitB of the two databases specified in the other parameters
@@ -128,6 +136,8 @@ func dbDiff(dbA string, dbB string) (Diffs, error) {
128
136
return Diffs {}, err
129
137
}
130
138
139
+ // TODO Check for differences in the PRAGMAs of both databases
140
+
131
141
// Return
132
142
return diff , nil
133
143
}
@@ -144,19 +154,28 @@ func diffSingleObject(sdb *sqlite.Conn, objectName string, objectType string) (b
144
154
var sqlInMain , sqlInAux string
145
155
err := sdb .OneValue ("SELECT sql FROM main.sqlite_master WHERE name = ? AND type = ?" , & sqlInMain , objectName , objectType )
146
156
if err != nil && err != io .EOF { // io.EOF is okay. It is returned when the object does not exist in the main database
147
- return false , diff , err
157
+ return false , DiffObjectChangeset {} , err
148
158
}
149
159
err = sdb .OneValue ("SELECT sql FROM aux.sqlite_master WHERE name = ? AND type = ?" , & sqlInAux , objectName , objectType )
150
160
if err != nil && err != io .EOF { // io.EOF is okay. It is returned when the object does not exist in the aux database
151
- return false , diff , err
161
+ return false , DiffObjectChangeset {} , err
152
162
}
153
163
154
164
// Check for dropped object
155
165
if sqlInMain != "" && sqlInAux == "" {
156
166
diff .Schema .ActionType = ACTION_DELETE
157
167
diff .Schema .Sql = "DROP " + strings .ToUpper (objectType ) + " " + EscapeId (objectName ) + ";"
158
168
159
- // No data changes for added objects so we can return here
169
+ // If this is a table, also add all the deleted data to the diff
170
+ if objectType == "table" {
171
+ // We never include the SQL statements because there is no need to delete all the rows when we DROP the table anyway
172
+ diff .Data , err = dataDiffForAllTableRows (sdb , "main" , objectName , ACTION_DELETE , false )
173
+ if err != nil {
174
+ return false , DiffObjectChangeset {}, err
175
+ }
176
+ }
177
+
178
+ // No further changes for dropped objects. So we can return here
160
179
return true , diff , nil
161
180
}
162
181
@@ -165,8 +184,15 @@ func diffSingleObject(sdb *sqlite.Conn, objectName string, objectType string) (b
165
184
diff .Schema .ActionType = ACTION_ADD
166
185
diff .Schema .Sql = sqlInAux + ";"
167
186
168
- // TODO If this is a table, also add all the data to the diff
187
+ // If this is a table, also add all the added data to the diff
188
+ if objectType == "table" {
189
+ diff .Data , err = dataDiffForAllTableRows (sdb , "aux" , objectName , ACTION_ADD , true )
190
+ if err != nil {
191
+ return false , DiffObjectChangeset {}, err
192
+ }
193
+ }
169
194
195
+ // No further changes for created objects. So we can return here
170
196
return true , diff , nil
171
197
}
172
198
@@ -177,13 +203,111 @@ func diffSingleObject(sdb *sqlite.Conn, objectName string, objectType string) (b
177
203
178
204
// TODO If this is a table, be more clever and try to get away with ALTER TABLE instead of DROP and CREATE
179
205
180
- // TODO If this is a table, also add all the data to the diff
206
+ // If this is a table, also add all the data to the diff
207
+ if objectType == "table" {
208
+ delete_data , err := dataDiffForAllTableRows (sdb , "main" , objectName , ACTION_DELETE , false )
209
+ if err != nil {
210
+ return false , DiffObjectChangeset {}, err
211
+ }
212
+ add_data , err := dataDiffForAllTableRows (sdb , "aux" , objectName , ACTION_ADD , true )
213
+ if err != nil {
214
+ return false , DiffObjectChangeset {}, err
215
+ }
216
+ diff .Data = append (delete_data , add_data ... )
217
+ }
181
218
219
+ // No further changes for modified objects. So we can return here
182
220
return true , diff , nil
183
221
}
184
222
185
- // TODO If this is a table, check for modified data
223
+ // If this is a table, check for modified data
224
+ if objectType == "table" {
225
+ // TODO
226
+ }
186
227
187
228
// Nothing has changed
188
229
return false , diff , nil
189
230
}
231
+
232
+ func dataDiffForAllTableRows (sdb * sqlite.Conn , schemaName string , tableName string , action DiffType , includeSql bool ) (diff []DataDiff , err error ) {
233
+ // Retrieve a list of all primary key columns in this table
234
+ pk , err := GetPrimaryKeyColumns (sdb , schemaName , tableName )
235
+ if err != nil {
236
+ return nil , err
237
+ }
238
+
239
+ // Escape all the column names
240
+ var pk_escaped []string
241
+ for _ , v := range pk {
242
+ pk_escaped = append (pk_escaped , EscapeId (v ))
243
+ }
244
+
245
+ // Prepare query for the primary keys of all rows in this table. Only include the rest of the data
246
+ // in the rows if required
247
+ query := "SELECT " + strings .Join (pk_escaped , "," )
248
+ if includeSql && action == ACTION_ADD {
249
+ query += ", *"
250
+ }
251
+ query += " FROM " + EscapeId (schemaName ) + "." + EscapeId (tableName )
252
+
253
+ // Retrieve data and add it to the data diff object
254
+ _ , _ , data , err := SQLiteRunQuery (sdb , Internal , query , false , false )
255
+ if err != nil {
256
+ log .Printf ("Error getting rows in dataDiffForAllTableRows(): %s\n " , err )
257
+ return nil , err
258
+ }
259
+ for _ , row := range data .Records {
260
+ var d DataDiff
261
+ d .ActionType = action
262
+
263
+ // Prepare SQL statement when needed
264
+ if includeSql {
265
+ if action == ACTION_DELETE {
266
+ d .Sql = "DELETE FROM " + EscapeId (tableName ) + " WHERE "
267
+ } else if action == ACTION_ADD {
268
+ d .Sql = "INSERT INTO " + EscapeId (tableName ) + " VALUES("
269
+ }
270
+ }
271
+
272
+ // Get primary key data
273
+ for i := 0 ; i < data .ColCount ; i ++ {
274
+ // If this column is still part of the primary key, add it to the data diff
275
+ if i < len (pk ) {
276
+ d .Pk = append (d .Pk , row [i ])
277
+ }
278
+
279
+ // If we want to include a SQL statement for deleting data and this is still
280
+ // part of the primary key, add this to the prepared DELETE statement
281
+ if includeSql && action == ACTION_DELETE && i < len (pk ) {
282
+ d .Sql += pk_escaped [i ];
283
+ if row [i ].Type == Null {
284
+ d .Sql += " IS NULL"
285
+ } else {
286
+ d .Sql += "=" + EscapeValue (row [i ])
287
+ }
288
+ d .Sql += " AND "
289
+ }
290
+
291
+ // If we want to include a SQL statement for adding data and this is the regular
292
+ // data part, add this to the prepared INSERT statement
293
+ if includeSql && action == ACTION_ADD && i >= len (pk ) {
294
+ d .Sql += EscapeValue (row [i ]) + ","
295
+ }
296
+ }
297
+
298
+ // Remove the last " AND " of the SQL query for DELETE statements and the last "," for INSERT statements
299
+ // and add a semicolon instead
300
+ if includeSql {
301
+ if action == ACTION_DELETE {
302
+ d .Sql = strings .TrimSuffix (d .Sql , " AND " ) + ";"
303
+ } else if action == ACTION_ADD {
304
+ d .Sql = strings .TrimSuffix (d .Sql , "," ) + ");"
305
+ }
306
+ }
307
+
308
+ // Add row to data diff set
309
+ diff = append (diff , d )
310
+ }
311
+
312
+ return diff , nil
313
+ }
0 commit comments