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

MerginMaps / geodiff / 3710958510

pending completion
3710958510

push

github

GitHub
allow python logger to set to None (#192)

3047 of 4066 relevant lines covered (74.94%)

409.27 hits per line

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

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

6
#include "changesetutils.h"
7

8
#include "base64utils.h"
9
#include "geodiffutils.hpp"
10
#include "changesetreader.h"
11
#include "changesetwriter.h"
12
#include "tableschema.h"
13

14

15
ChangesetTable schemaToChangesetTable( const std::string &tableName, const TableSchema &tbl )
185✔
16
{
17
  ChangesetTable chTable;
185✔
18
  chTable.name = tableName;
185✔
19
  for ( const TableColumnInfo &c : tbl.columns )
897✔
20
    chTable.primaryKeys.push_back( c.isPrimaryKey );
712✔
21
  return chTable;
185✔
22
}
×
23

24
void invertChangeset( ChangesetReader &reader, ChangesetWriter &writer )
33✔
25
{
26
  std::string currentTableName;
33✔
27
  std::vector<bool> currentPkeys;
33✔
28
  ChangesetEntry entry;
33✔
29
  while ( reader.nextEntry( entry ) )
108✔
30
  {
31
    assert( entry.table );
75✔
32
    if ( entry.table->name != currentTableName )
75✔
33
    {
34
      writer.beginTable( *entry.table );
37✔
35
      currentTableName = entry.table->name;
37✔
36
      currentPkeys = entry.table->primaryKeys;
37✔
37
    }
38

39
    if ( entry.op == ChangesetEntry::OpInsert )
75✔
40
    {
41
      ChangesetEntry out;
48✔
42
      out.op = ChangesetEntry::OpDelete;
48✔
43
      out.oldValues = entry.newValues;
48✔
44
      writer.writeEntry( out );
48✔
45
    }
48✔
46
    else if ( entry.op == ChangesetEntry::OpDelete )
27✔
47
    {
48
      ChangesetEntry out;
9✔
49
      out.op = ChangesetEntry::OpInsert;
9✔
50
      out.newValues = entry.oldValues;
9✔
51
      writer.writeEntry( out );
9✔
52
    }
9✔
53
    else if ( entry.op == ChangesetEntry::OpUpdate )
18✔
54
    {
55
      ChangesetEntry out;
18✔
56
      out.op = ChangesetEntry::OpUpdate;
18✔
57
      out.newValues = entry.oldValues;
18✔
58
      out.oldValues = entry.newValues;
18✔
59
      // if a column is a part of pkey and has not been changed,
60
      // the original entry has "old" value the pkey value and "new"
61
      // value is undefined - let's reverse "old" and "new" in that case.
62
      for ( size_t i = 0; i < currentPkeys.size(); ++i )
86✔
63
      {
64
        if ( currentPkeys[i] && out.oldValues[i].type() == Value::TypeUndefined )
68✔
65
        {
66
          out.oldValues[i] = out.newValues[i];
18✔
67
          out.newValues[i].setUndefined();
18✔
68
        }
69
      }
70
      writer.writeEntry( out );
18✔
71
    }
18✔
72
    else
73
    {
74
      throw GeoDiffException( "Unknown entry operation!" );
×
75
    }
76
  }
77
}
33✔
78

79
nlohmann::json valueToJSON( const Value &value )
461✔
80
{
81
  nlohmann::json j;
461✔
82
  switch ( value.type() )
461✔
83
  {
84
    case Value::TypeUndefined:
194✔
85
      break;  // actually this not get printed - undefined value should be omitted completely
194✔
86
    case Value::TypeInt:
111✔
87
      j = value.getInt();
111✔
88
      break;
111✔
89
    case Value::TypeDouble:
9✔
90
      j = value.getDouble();
9✔
91
      break;
9✔
92
    case Value::TypeText:
57✔
93
      j = value.getString();
57✔
94
      break;
57✔
95
    case Value::TypeBlob:
57✔
96
    {
97
      // this used to either show "blob N bytes" or would be converted to WKT
98
      // but this is better - it preserves content of any type + can be decoded back
99
      std::string base64 = base64_encode( ( const unsigned char * ) value.getString().data(), ( unsigned int ) value.getString().size() );
57✔
100
      j = base64;
57✔
101
      break;
57✔
102
    }
57✔
103
    case Value::TypeNull:
33✔
104
      j = "null";
33✔
105
      break;
33✔
106
    default:
×
107
      j = "(unknown)";  // should never happen
×
108
  }
109
  return j;
461✔
110
}
×
111

112

113
nlohmann::json changesetEntryToJSON( const ChangesetEntry &entry )
66✔
114
{
115
  std::string status;
66✔
116
  if ( entry.op == ChangesetEntry::OpUpdate )
66✔
117
    status = "update";
20✔
118
  else if ( entry.op == ChangesetEntry::OpInsert )
46✔
119
    status = "insert";
37✔
120
  else if ( entry.op == ChangesetEntry::OpDelete )
9✔
121
    status = "delete";
9✔
122

123
  nlohmann::json res;
66✔
124
  res[ "table" ] = entry.table->name;
66✔
125
  res[ "type" ] = status;
66✔
126

127
  auto entries = nlohmann::json::array();
66✔
128

129
  Value valueOld, valueNew;
66✔
130
  for ( size_t i = 0; i < entry.table->columnCount(); ++i )
322✔
131
  {
132
    valueNew = ( entry.op == ChangesetEntry::OpUpdate || entry.op == ChangesetEntry::OpInsert ) ? entry.newValues[i] : Value();
476✔
133
    valueOld = ( entry.op == ChangesetEntry::OpUpdate || entry.op == ChangesetEntry::OpDelete ) ? entry.oldValues[i] : Value();
374✔
134

135
    nlohmann::json change;
256✔
136

137
    if ( valueNew.type() != Value::TypeUndefined || valueOld.type() != Value::TypeUndefined )
256✔
138
    {
139
      change[ "column" ] = i;
223✔
140

141
      nlohmann::json jsonValueOld = valueToJSON( valueOld );
223✔
142
      nlohmann::json jsonValueNew = valueToJSON( valueNew );
223✔
143

144
      if ( !jsonValueOld.empty() )
223✔
145
      {
146
        if ( jsonValueOld == "null" )
85✔
147
          change[ "old" ] = nullptr;
1✔
148
        else
149
          change[ "old" ] = jsonValueOld;
84✔
150
      }
151
      if ( !jsonValueNew.empty() )
223✔
152
      {
153
        if ( jsonValueNew == "null" )
167✔
154
          change[ "new" ] = nullptr;
32✔
155
        else
156
          change[ "new" ] = jsonValueNew;
135✔
157
      }
158

159
      entries.push_back( change );
223✔
160
    }
223✔
161
  }
256✔
162

163
  res[ "changes" ] = entries;
66✔
164
  return res;
132✔
165
}
66✔
166

167
nlohmann::json changesetToJSON( ChangesetReader &reader )
29✔
168
{
169
  auto entries = nlohmann::json::array();
29✔
170

171
  ChangesetEntry entry;
29✔
172
  while ( reader.nextEntry( entry ) )
92✔
173
  {
174
    nlohmann::json msg = changesetEntryToJSON( entry );
63✔
175
    if ( msg.empty() )
63✔
176
      continue;
×
177

178
    entries.push_back( msg );
63✔
179
  }
63✔
180

181
  nlohmann::json res;
29✔
182
  res[ "geodiff" ] = entries;
29✔
183
  return res;
58✔
184
}
29✔
185

186
//! auxiliary table used to create table changes summary
187
struct TableSummary
188
{
189
  TableSummary() : inserts( 0 ), updates( 0 ), deletes( 0 ) {}
24✔
190
  int inserts;
191
  int updates;
192
  int deletes;
193
};
194

195
nlohmann::json changesetToJSONSummary( ChangesetReader &reader )
23✔
196
{
197
  std::map< std::string, TableSummary > summary;
23✔
198

199
  ChangesetEntry entry;
23✔
200
  while ( reader.nextEntry( entry ) )
78✔
201
  {
202
    std::string tableName = entry.table->name;
55✔
203
    TableSummary &tableSummary = summary[tableName];
55✔
204

205
    if ( entry.op == ChangesetEntry::OpUpdate )
55✔
206
      ++tableSummary.updates;
16✔
207
    else if ( entry.op == ChangesetEntry::OpInsert )
39✔
208
      ++tableSummary.inserts;
33✔
209
    else if ( entry.op == ChangesetEntry::OpDelete )
6✔
210
      ++tableSummary.deletes;
6✔
211
  }
55✔
212

213
  // write JSON
214
  auto entries = nlohmann::json::array();
23✔
215
  for ( const auto &kv : summary )
47✔
216
  {
217
    nlohmann::json tableJson;
24✔
218
    tableJson[ "table" ] = kv.first;
24✔
219
    tableJson[ "insert" ] = kv.second.inserts;
24✔
220
    tableJson[ "update" ] = kv.second.updates;
24✔
221
    tableJson[ "delete" ] = kv.second.deletes;
24✔
222

223
    entries.push_back( tableJson );
24✔
224
  }
24✔
225
  nlohmann::json res;
23✔
226
  res[ "geodiff_summary" ] = entries;
23✔
227
  return res;
46✔
228
}
23✔
229

230
nlohmann::json conflictToJSON( const ConflictFeature &conflict )
4✔
231
{
232
  nlohmann::json res;
4✔
233
  res[ "table" ] = std::string( conflict.tableName() );
4✔
234
  res[ "type" ] = "conflict";
4✔
235
  res[ "fid" ] = std::to_string( conflict.pk() );
4✔
236

237
  auto entries = nlohmann::json::array();
4✔
238

239
  const std::vector<ConflictItem> items = conflict.items();
4✔
240
  for ( const ConflictItem &item : items )
9✔
241
  {
242
    nlohmann::json change;
5✔
243
    change[ "column" ] = item.column();
5✔
244

245
    nlohmann::json valueBase = valueToJSON( item.base() );
5✔
246
    nlohmann::json valueOld = valueToJSON( item.theirs() );
5✔
247
    nlohmann::json valueNew = valueToJSON( item.ours() );
5✔
248

249
    if ( !valueBase.empty() )
5✔
250
    {
251
      if ( valueBase == "null" )
5✔
252
        change[ "base" ] = nullptr;
×
253
      else
254
        change[ "base" ] = valueBase;
5✔
255
    }
256
    if ( !valueOld.empty() )
5✔
257
    {
258
      if ( valueOld == "null" )
5✔
259
        change[ "old" ] = nullptr;
×
260
      else
261
        change[ "old" ] = valueOld;
5✔
262
    }
263
    if ( !valueNew.empty() )
5✔
264
    {
265
      if ( valueNew == "null" )
5✔
266
        change[ "new" ] = nullptr;
×
267
      else
268
        change[ "new" ] = valueNew;
5✔
269
    }
270

271
    entries.push_back( change );
5✔
272
  }
5✔
273
  res[ "changes" ] = entries;
4✔
274
  return res;
8✔
275
}
4✔
276

277
nlohmann::json conflictsToJSON( const std::vector<ConflictFeature> &conflicts )
4✔
278
{
279
  auto entries = nlohmann::json::array();
4✔
280
  for ( const ConflictFeature &item : conflicts )
8✔
281
  {
282
    nlohmann::json msg = conflictToJSON( item );
4✔
283
    if ( msg.empty() )
4✔
284
      continue;
×
285

286
    entries.push_back( msg );
4✔
287
  }
4✔
288

289
  nlohmann::json res;
4✔
290
  res[ "geodiff" ] = entries;
4✔
291
  return res;
8✔
292
}
4✔
293

294
inline int hex2num( unsigned char i )
14,106✔
295
{
296
  if ( i <= '9' && i >= '0' )
14,106✔
297
    return i - '0';
10,733✔
298
  if ( i >= 'A' && i <= 'F' )
3,373✔
299
    return 10 + i - 'A';
2✔
300
  if ( i >= 'a' && i <= 'f' )
3,371✔
301
    return 10 + i - 'a';
3,371✔
302
  assert( false );
×
303
  return 0; // should never happen
304
}
305

306
inline char num2hex( int n )
16,724✔
307
{
308
  assert( n >= 0 && n < 16 );
16,724✔
309
  if ( n >= 0 && n < 10 )
16,724✔
310
    return char( '0' + n );
12,753✔
311
  else if ( n >= 10 && n < 16 )
3,971✔
312
    return char( 'A' + n - 10 );
3,971✔
313
  return '?';  // should never happen
×
314
}
315

316
std::string hex2bin( const std::string &str )
75✔
317
{
318
  assert( str.size() % 2 == 0 );
75✔
319
  std::string output( str.size() / 2, 0 );
75✔
320
  for ( size_t i = 0; i < str.size(); i += 2 )
7,128✔
321
  {
322
    int n1 = hex2num( str[i] ), n2 = hex2num( str[i + 1] );
7,053✔
323
    output[i / 2] = char( n1 * 16 + n2 );
7,053✔
324
  }
325
  return output;
75✔
326
}
×
327

328
std::string bin2hex( const std::string &str )
77✔
329
{
330
  std::string output( str.size() * 2, 0 );
77✔
331
  for ( size_t i = 0; i < str.size(); ++i )
8,439✔
332
  {
333
    unsigned char ch = str[i];
8,362✔
334
    output[i * 2] = num2hex( ch / 16 );
8,362✔
335
    output[i * 2 + 1] = num2hex( ch % 16 );
8,362✔
336
  }
337
  return output;
77✔
338
}
×
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

© 2025 Coveralls, Inc