• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MerginMaps / geodiff / 27405271896

12 Jun 2026 08:49AM UTC coverage: 87.793% (-0.3%) from 88.114%
27405271896

Pull #252

github

web-flow
Merge 9be23187a into 0a94b5ba4
Pull Request #252: Allow schema changes in diffs

1096 of 1222 new or added lines in 13 files covered. (89.69%)

13 existing lines in 3 files now uncovered.

4272 of 4866 relevant lines covered (87.79%)

615.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.44
/geodiff/src/changesetwriter.cpp
1
/*
2
 GEODIFF - MIT License
3
 Copyright (C) 2020 Martin Dobias
4
*/
5

6
#include "changesetwriter.h"
7

8
#include "changeset.h"
9
#include "geodiffutils.hpp"
10
#include "changesetputvarint.h"
11
#include "portableendian.h"
12

13
#include <assert.h>
14
#include <memory.h>
15

16
#include <sstream>
17
#include <variant>
18

19
void ChangesetWriter::open( const std::string &filename )
542✔
20
{
21
#ifdef WIN32
22
  mFile.open( stringToWString( filename ), std::ios::out | std::ios::binary );
23
#else
24
  mFile.open( filename, std::ios::out | std::ios::binary );
542✔
25
#endif
26
  if ( !mFile.is_open() )
542✔
27
    throw GeoDiffException( "Unable to open changeset file for writing: " + filename );
×
28
}
542✔
29

30
void ChangesetWriter::beginTable( const ChangesetTable &table )
523✔
31
{
32
  mCurrentTable = table;
523✔
33

34
  writeByte( ( int ) ChangesetEntryType::OpTableRecord );
523✔
35
  writeVarint( ( int ) table.columnCount() );
523✔
36
  for ( size_t i = 0; i < table.columnCount(); ++i )
2,371✔
37
    writeByte( table.primaryKeys[i] );
1,848✔
38
  writeNullTerminatedString( table.name );
523✔
39
}
523✔
40

41
void ChangesetWriter::writeEntry( const ChangesetEntry &entry )
987✔
42
{
43
  if ( const ChangesetDataEntry *dataEntry = std::get_if<ChangesetDataEntry>( &entry ) )
987✔
44
    writeDataEntry( *dataEntry );
956✔
45
  else if ( const ChangesetCreateTableEntry *ctEntry = std::get_if<ChangesetCreateTableEntry>( &entry ) )
31✔
46
    writeCreateTableEntry( *ctEntry );
8✔
47
  else if ( const ChangesetDropTableEntry *dtEntry = std::get_if<ChangesetDropTableEntry>( &entry ) )
23✔
48
    writeDropTableEntry( *dtEntry );
6✔
49
  else if ( const ChangesetAddColumnEntry *acEntry = std::get_if<ChangesetAddColumnEntry>( &entry ) )
17✔
50
    writeAddColumnEntry( *acEntry );
9✔
51
  else if ( const ChangesetDropColumnEntry *dcEntry = std::get_if<ChangesetDropColumnEntry>( &entry ) )
8✔
52
    writeDropColumnEntry( *dcEntry );
8✔
53
  else
NEW
54
    throw GeoDiffException( "Tried to write unhandled changeset entry type! " +
×
NEW
55
                            std::to_string( entry.index() ) );
×
56
}
987✔
57

58
void ChangesetWriter::writeByte( char c )
8,648✔
59
{
60
  mFile.write( &c, 1 );
8,648✔
61
}
8,648✔
62

63
void ChangesetWriter::writeVarint( int n )
2,117✔
64
{
65
  unsigned char output[9];  // 1-9 bytes
66
  int numBytes = putVarint32( output, n );
2,117✔
67
  mFile.write( reinterpret_cast<char *>( output ), numBytes );
2,117✔
68
}
2,117✔
69

70
void ChangesetWriter::writeNullTerminatedString( const std::string &str )
672✔
71
{
72
  mFile.write( str.c_str(), str.size() + 1 );
672✔
73
}
672✔
74

75
void ChangesetWriter::writeRowValues( const std::vector<Value> &values )
1,185✔
76
{
77
  if ( values.size() != mCurrentTable.columnCount() )
1,185✔
78
    throw GeoDiffException( "wrong number of rows in the entry" );
×
79

80
  for ( size_t i = 0; i < mCurrentTable.columnCount(); ++i )
5,401✔
81
  {
82
    Value::Type type = values[i].type();
4,216✔
83
    writeByte( ( char ) type );
4,216✔
84
    if ( type == Value::TypeInt ) // 0x01
4,216✔
85
    {
86
      // 64-bit int (big endian)
87
      uint64_t x;
88
      int64_t v = values[i].getInt();
1,560✔
89
      memcpy( &x, &v, 8 );
1,560✔
90
      x = htobe64( x ); // convert host to big endian
1,560✔
91
      mFile.write( reinterpret_cast<char *>( &x ), 8 );
1,560✔
92
    }
93
    else if ( type == Value::TypeDouble ) // 0x02
2,656✔
94
    {
95
      // 64-bit double (big endian)
96
      int64_t x;
97
      double v = values[i].getDouble();
35✔
98
      memcpy( &x, &v, 8 );
35✔
99
      x = htobe64( x ); // convert host to big endian
35✔
100
      mFile.write( reinterpret_cast<char *>( &x ), 8 );
35✔
101
    }
102
    else if ( type == Value::TypeText || type == Value::TypeBlob ) // 0x03 or 0x04
2,621✔
103
    {
104
      const std::string &str = values[i].getString();
1,521✔
105
      writeVarint( static_cast<int>( str.size() ) );
1,521✔
106
      mFile.write( str.c_str(), str.size() );
1,521✔
107
    }
1,521✔
108
    else if ( type == Value::TypeNull ) // 0x05
1,100✔
109
    {
110
      // nothing extra to write
111
    }
112
    else if ( type == Value::TypeUndefined )  // undefined value  (different from NULL)
833✔
113
    {
114
      // nothing extra to write
115
    }
116
    else
117
    {
118
      throw GeoDiffException( "unexpected entry type" );
×
119
    }
120
  }
121
}
1,185✔
122

123
void ChangesetWriter::writeColumnInfo( const TableColumnInfo &column )
59✔
124
{
125
  writeNullTerminatedString( column.name );
59✔
126
  writeByte( static_cast<char>( column.type.baseType ) );
59✔
127
  writeByte( ( column.isPrimaryKey << 0 )
59✔
128
             | ( column.isNotNull << 1 )
59✔
129
             | ( column.isAutoIncrement << 2 )
59✔
130
             | ( column.isGeometry << 3 )
59✔
131
             | ( column.geomHasZ << 4 )
59✔
132
             | ( column.geomHasM << 5 ) );
59✔
133
  writeNullTerminatedString( column.geomType );
59✔
134
  writeVarint( column.geomSrsId );
59✔
135
}
59✔
136

137

138
void ChangesetWriter::writeDataEntry( const ChangesetDataEntry &entry )
956✔
139
{
140
  if ( entry.op != ChangesetDataEntry::OpInsert && entry.op != ChangesetDataEntry::OpUpdate && entry.op != ChangesetDataEntry::OpDelete )
956✔
NEW
141
    throw GeoDiffException( "wrong op for changeset entry" );
×
142
  writeByte( ( char ) entry.op );
956✔
143
  writeByte( 0 );  // "indirect" always false
956✔
144

145
  if ( entry.op != ( int ) ChangesetEntryType::OpInsert )
956✔
146
    writeRowValues( entry.oldValues );
401✔
147
  if ( entry.op != ( int ) ChangesetEntryType::OpDelete )
956✔
148
    writeRowValues( entry.newValues );
784✔
149
}
956✔
150

151
void ChangesetWriter::writeCreateTableEntry( const ChangesetCreateTableEntry &entry )
8✔
152
{
153
  writeByte( static_cast<char>( ChangesetEntryType::OpCreateTable ) );
8✔
154
  writeNullTerminatedString( entry.tableName );
8✔
155
  writeVarint( static_cast<int>( entry.columns.size() ) );
8✔
156
  for ( const TableColumnInfo &column : entry.columns )
32✔
157
  {
158
    writeColumnInfo( column );
24✔
159
  }
160
}
8✔
161

162
void ChangesetWriter::writeDropTableEntry( const ChangesetDropTableEntry &entry )
6✔
163
{
164
  writeByte( static_cast<char>( ChangesetEntryType::OpDropTable ) );
6✔
165
  writeNullTerminatedString( entry.tableName );
6✔
166
  writeVarint( static_cast<int>( entry.columns.size() ) );
6✔
167
  for ( const TableColumnInfo &column : entry.columns )
24✔
168
  {
169
    writeColumnInfo( column );
18✔
170
  }
171
}
6✔
172

173
void ChangesetWriter::writeAddColumnEntry( const ChangesetAddColumnEntry &entry )
9✔
174
{
175
  writeByte( static_cast<char>( ChangesetEntryType::OpAddColumn ) );
9✔
176
  writeNullTerminatedString( entry.tableName );
9✔
177
  writeColumnInfo( entry.column );
9✔
178
}
9✔
179

180
void ChangesetWriter::writeDropColumnEntry( const ChangesetDropColumnEntry &entry )
8✔
181
{
182
  writeByte( static_cast<char>( ChangesetEntryType::OpDropColumn ) );
8✔
183
  writeNullTerminatedString( entry.tableName );
8✔
184
  writeColumnInfo( entry.column );
8✔
185
}
8✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc