Skip to content

Commit 45d4ea1

Browse files
committed
Change column change entries to store indices as well
We need this to produce consistent column orders after diff application and after inverse of drop.
1 parent 299ef3f commit 45d4ea1

17 files changed

Lines changed: 619 additions & 117 deletions

‎docs/changeset-format.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ This entry adds a new column to an existing table. All existing rows will have
8787

8888
- 1 byte: Constant 0x63 (lowercase 'c')
8989
- Null-terminated string: Table name
90+
- Varint: Zero-based index the column has after being added.
9091
- Table column info.
9192

9293
## Drop column entry
@@ -97,6 +98,7 @@ rebasing and inverting the changeset.
9798

9899
- 1 byte: Constant 0x43 (uppercase 'C')
99100
- Null-terminated string: Table name
101+
- Varint: Zero-based index the column has before being dropped.
100102
- Table column info.
101103

102104
# Record Format

‎docs/schema-changes.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
Geodiff supports diffing databases with different schemata. It identifies table
44
and column additions/deletions.
55

6+
Columns are identified by their index as well as their name, and are added and
7+
dropped at a specific index, so that applying a diff reproduces the column order
8+
of the modified database.
9+
610
Tables and columns are always created empty and any data present in the
711
database is recreated manually via `INSERT`/`UPDATE` entries, written after the
812
schema change entry. Likewise, deletion entries expect the table/column to be
@@ -24,3 +28,8 @@ be moved. Same with renaming tables.
2428

2529
The intermediate states created by applying the resulting diff (e.g. "nulling
2630
out" column before dropping it) may conflict with database constraints.
31+
32+
Adding a column somewhere else than at the end of the column list is not always
33+
possible without modifying the database around it (e.g. dropping constraints).
34+
We currently don't do that and just fail if a column addition is not easily
35+
possible.

‎geodiff/src/changeset.h‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,13 +287,17 @@ struct ChangesetDropTableEntry
287287
struct ChangesetAddColumnEntry
288288
{
289289
std::string tableName;
290+
//! Zero-based index of new column after creation
291+
size_t columnIdx;
290292
TableColumnInfo column;
291293
};
292294

293295
//! Entry for ALTER TABLE ... DROP COLUMN command
294296
struct ChangesetDropColumnEntry
295297
{
296298
std::string tableName;
299+
//! Zero-based index of column before deletion
300+
size_t columnIdx;
297301
TableColumnInfo column;
298302
};
299303

@@ -320,6 +324,19 @@ struct ChangesetEntry : public std::variant <
320324
return ChangesetEntryType::OpDropColumn;
321325
throw std::invalid_argument( "Unreachable - operationType()" );
322326
}
327+
328+
std::string schemaChangeTableName() const
329+
{
330+
if ( const ChangesetCreateTableEntry *ctEntry = std::get_if<ChangesetCreateTableEntry>( this ) )
331+
return ctEntry->tableName;
332+
else if ( const ChangesetDropTableEntry *dtEntry = std::get_if<ChangesetDropTableEntry>( this ) )
333+
return dtEntry->tableName;
334+
else if ( const ChangesetAddColumnEntry *acEntry = std::get_if<ChangesetAddColumnEntry>( this ) )
335+
return acEntry->tableName;
336+
else if ( const ChangesetDropColumnEntry *dcEntry = std::get_if<ChangesetDropColumnEntry>( this ) )
337+
return dcEntry->tableName;
338+
return "";
339+
}
323340
};
324341

325342
#endif // CHANGESET_H

‎geodiff/src/changesetconcat.cpp‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -246,9 +246,9 @@ void concatChangesets(
246246
}
247247
else if ( ChangesetDropColumnEntry *dcEntry = std::get_if<ChangesetDropColumnEntry>( &fullEntry ) )
248248
{
249-
// This entry only contains the column's name, not its index, so we
250-
// can't apply its effects to the existing entries. The best we can do
251-
// is just forward this entry.
249+
// Short-circuting column removals is hard, since we can't just prepend
250+
// it to other entries (the column needs to be NULLed out first). So
251+
// for now we treat it as a barrier.
252252
phase[dcEntry->tableName].entries.push_back( *dcEntry );
253253
// We also need to start a new phase, since we can't merge entries
254254
// anymore.
@@ -273,9 +273,9 @@ void concatChangesets(
273273
newColumnCount = existingDEntry->table->columnCount() + 1;
274274
if ( existingDEntry->table->columnCount() != newColumnCount )
275275
existingDEntry->table->primaryKeys.push_back( false );
276-
if ( existingDEntry->oldValues.size() != newColumnCount )
276+
if ( existingDEntry->oldValues.size() != 0 && existingDEntry->oldValues.size() != newColumnCount )
277277
existingDEntry->oldValues.push_back( Value::makeNull() );
278-
if ( existingDEntry->newValues.size() != newColumnCount )
278+
if ( existingDEntry->newValues.size() != 0 && existingDEntry->newValues.size() != newColumnCount )
279279
existingDEntry->newValues.push_back( Value::makeNull() );
280280
}
281281
}

‎geodiff/src/changesetreader.cpp‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ ChangesetAddColumnEntry ChangesetReader::readAddColumnEntry()
280280
{
281281
ChangesetAddColumnEntry entry;
282282
entry.tableName = readNullTerminatedString();
283+
entry.columnIdx = readVarint();
283284
entry.column = readColumnInfo();
284285
return entry;
285286
}
@@ -288,6 +289,7 @@ ChangesetDropColumnEntry ChangesetReader::readDropColumnEntry()
288289
{
289290
ChangesetDropColumnEntry entry;
290291
entry.tableName = readNullTerminatedString();
292+
entry.columnIdx = readVarint();
291293
entry.column = readColumnInfo();
292294
return entry;
293295
}

‎geodiff/src/changesetutils.cpp‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ std::vector<ChangesetEntry> invertChangesetReverse( ChangesetReader &reader )
8383
{
8484
ChangesetDropColumnEntry out;
8585
out.tableName = acEntry->tableName;
86+
// The index of the added column before its deletion is the same as after
87+
// its addition, so it can be carried over as-is.
88+
out.columnIdx = acEntry->columnIdx;
8689
out.column = acEntry->column;
8790
invertedEntries.push_back( out );
8891
}
@@ -97,6 +100,7 @@ std::vector<ChangesetEntry> invertChangesetReverse( ChangesetReader &reader )
97100
{
98101
ChangesetAddColumnEntry out;
99102
out.tableName = dcEntry->tableName;
103+
out.columnIdx = dcEntry->columnIdx;
100104
out.column = dcEntry->column;
101105
invertedEntries.push_back( out );
102106
}
@@ -277,6 +281,7 @@ nlohmann::json changesetEntryToJSON( const ChangesetEntry &entry )
277281
nlohmann::json res;
278282
res["type"] = "add_column";
279283
res["tableName"] = acEntry->tableName;
284+
res["columnIdx"] = acEntry->columnIdx;
280285
res["column"] = columnInfoToJSON( acEntry->column );
281286
return res;
282287
}
@@ -285,6 +290,7 @@ nlohmann::json changesetEntryToJSON( const ChangesetEntry &entry )
285290
nlohmann::json res;
286291
res["type"] = "drop_column";
287292
res["tableName"] = dcEntry->tableName;
293+
res["columnIdx"] = dcEntry->columnIdx;
288294
res["column"] = columnInfoToJSON( dcEntry->column );
289295
return res;
290296
}

‎geodiff/src/changesetwriter.cpp‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,14 @@ void ChangesetWriter::writeAddColumnEntry( const ChangesetAddColumnEntry &entry
174174
{
175175
writeByte( static_cast<char>( ChangesetEntryType::OpAddColumn ) );
176176
writeNullTerminatedString( entry.tableName );
177+
writeVarint( entry.columnIdx );
177178
writeColumnInfo( entry.column );
178179
}
179180

180181
void ChangesetWriter::writeDropColumnEntry( const ChangesetDropColumnEntry &entry )
181182
{
182183
writeByte( static_cast<char>( ChangesetEntryType::OpDropColumn ) );
183184
writeNullTerminatedString( entry.tableName );
185+
writeVarint( entry.columnIdx );
184186
writeColumnInfo( entry.column );
185187
}

0 commit comments

Comments
 (0)