~sil/u1db/cors-headers-and-web-admin

« back to all changes in this revision

Viewing changes to src/u1db.c

Merge the actual content from sqlite-c-backend, but get rid of all the conflicting stuff.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright 2011 Canonical Ltd.
 
3
 * 
 
4
 * This program is free software: you can redistribute it and/or modify it
 
5
 * under the terms of the GNU General Public License version 3, as published
 
6
 * by the Free Software Foundation.
 
7
 * 
 
8
 * This program is distributed in the hope that it will be useful, but
 
9
 * WITHOUT ANY WARRANTY; without even the implied warranties of
 
10
 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 
11
 * PURPOSE.  See the GNU General Public License for more details.
 
12
 * 
 
13
 * You should have received a copy of the GNU General Public License along
 
14
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 */
 
16
 
 
17
#include "compat.h"
 
18
 
 
19
#include <string.h>
 
20
#include <stdio.h>
 
21
#include <stdlib.h>
 
22
#include <sqlite3.h>
 
23
#include "u1db.h"
 
24
 
 
25
struct _u1database
 
26
{
 
27
    sqlite3 *sql_handle;
 
28
    char *machine_id;
 
29
};
 
30
 
 
31
static const char *table_definitions[] = {
 
32
    "CREATE TABLE transaction_log ("
 
33
    " db_rev INTEGER PRIMARY KEY AUTOINCREMENT,"
 
34
    " doc_id TEXT)",
 
35
    "CREATE TABLE document ("
 
36
    " doc_id TEXT PRIMARY KEY,"
 
37
    " doc_rev TEXT,"
 
38
    " doc TEXT)",
 
39
    "CREATE TABLE document_fields ("
 
40
    " doc_id TEXT,"
 
41
    " field_name TEXT,"
 
42
    " value TEXT,"
 
43
    " CONSTRAINT document_fields_pkey"
 
44
    " PRIMARY KEY (doc_id, field_name))",
 
45
    "CREATE TABLE sync_log ("
 
46
    " machine_id TEXT PRIMARY KEY,"
 
47
    " known_db_rev INTEGER)",
 
48
    "CREATE TABLE conflicts ("
 
49
    " doc_id TEXT,"
 
50
    " doc_rev TEXT,"
 
51
    " doc TEXT,"
 
52
    " CONSTRAINT conflicts_pkey PRIMARY KEY (doc_id, doc_rev))",
 
53
    "CREATE TABLE index_definitions ("
 
54
    " name TEXT,"
 
55
    " offset INT,"
 
56
    " field TEXT,"
 
57
    " CONSTRAINT index_definitions_pkey"
 
58
    " PRIMARY KEY (name, offset))",
 
59
    "CREATE TABLE u1db_config (name TEXT, value TEXT)",
 
60
    "INSERT INTO u1db_config VALUES ('sql_schema', '0')",
 
61
};
 
62
 
 
63
static int
 
64
initialize(u1database *db)
 
65
{
 
66
    sqlite3_stmt *statement;
 
67
    int i, status, final_status;
 
68
 
 
69
    for(i = 0; i < sizeof(table_definitions)/sizeof(char*); i++) {
 
70
        status = sqlite3_prepare_v2(db->sql_handle,
 
71
            table_definitions[i], -1, &statement, NULL);
 
72
        if(status != SQLITE_OK) {
 
73
            return status;
 
74
        }
 
75
        status = sqlite3_step(statement);
 
76
        final_status = sqlite3_finalize(statement);
 
77
        if(status != SQLITE_DONE) {
 
78
            return status;
 
79
        }
 
80
        if(final_status != SQLITE_OK) {
 
81
            return final_status;
 
82
        }
 
83
    }
 
84
    return SQLITE_OK;
 
85
}
 
86
 
 
87
u1database *
 
88
u1db_open(const char *fname)
 
89
{
 
90
    u1database *db = (u1database *)(calloc(1, sizeof(u1database)));
 
91
    int status;
 
92
    status = sqlite3_open(fname, &db->sql_handle);
 
93
    if(status != SQLITE_OK) {
 
94
        // What do we do here?
 
95
        free(db);
 
96
        return NULL;
 
97
    }
 
98
    initialize(db);
 
99
    return db;
 
100
}
 
101
 
 
102
// Windows doesn't have strndup, so we fake one
 
103
char *_win32_strndup(const char *s, size_t n)
 
104
{
 
105
    char *out;
 
106
    out = (char*)malloc(n+1);
 
107
    if (out == NULL) {
 
108
        return NULL;
 
109
    }
 
110
    memcpy(out, s, n);
 
111
    out[n] = '\0';
 
112
    return out;
 
113
}
 
114
 
 
115
int
 
116
u1db__sql_close(u1database *db)
 
117
{
 
118
    if (db->sql_handle != NULL) {
 
119
        // sqlite says closing a NULL handle is ok, but we don't want to trust that
 
120
        int status;
 
121
        status = sqlite3_close(db->sql_handle);
 
122
        db->sql_handle = NULL;
 
123
        return status;
 
124
    }
 
125
    return SQLITE_OK;
 
126
}
 
127
 
 
128
int 
 
129
u1db__sql_is_open(u1database *db)
 
130
{
 
131
    if (db != NULL && db->sql_handle != NULL) {
 
132
        // The handle is still open
 
133
        return 1;
 
134
    }
 
135
    return 0;
 
136
}
 
137
 
 
138
void
 
139
u1db_free(u1database **db)
 
140
{
 
141
    if (db == NULL || *db == NULL) {
 
142
        return;
 
143
    }
 
144
    free((*db)->machine_id);
 
145
    u1db__sql_close(*db);
 
146
    free(*db);
 
147
    *db = NULL;
 
148
}
 
149
 
 
150
int
 
151
u1db_set_machine_id(u1database *db, const char *machine_id)
 
152
{
 
153
    sqlite3_stmt *statement;
 
154
    int status, final_status, num_bytes;
 
155
    status = sqlite3_prepare_v2(db->sql_handle,
 
156
        "INSERT INTO u1db_config VALUES (?, ?)", -1,
 
157
        &statement, NULL); 
 
158
    if (status != SQLITE_OK) {
 
159
        return status;
 
160
    }
 
161
    status = sqlite3_bind_text(statement, 1, "machine_id", -1, SQLITE_STATIC);
 
162
    if (status != SQLITE_OK) {
 
163
        sqlite3_finalize(statement);
 
164
        return status;
 
165
    }
 
166
    status = sqlite3_bind_text(statement, 2, machine_id, -1, SQLITE_TRANSIENT);
 
167
    if (status != SQLITE_OK) {
 
168
        sqlite3_finalize(statement);
 
169
        return status;
 
170
    }
 
171
    status = sqlite3_step(statement);
 
172
    final_status = sqlite3_finalize(statement);
 
173
    if (status != SQLITE_DONE) {
 
174
        return status;
 
175
    }
 
176
    if (final_status != SQLITE_OK) {
 
177
        return final_status;
 
178
    }
 
179
    // If we got this far, then machine_id has been properly set. Copy it
 
180
    if (db->machine_id != NULL) {
 
181
        free(db->machine_id);
 
182
    }
 
183
    num_bytes = strlen(machine_id);
 
184
    db->machine_id = (char *)calloc(1, num_bytes + 1);
 
185
    memcpy(db->machine_id, machine_id, num_bytes + 1);
 
186
    return 0;
 
187
}
 
188
 
 
189
int
 
190
u1db_get_machine_id(u1database *db, char **machine_id)
 
191
{
 
192
    sqlite3_stmt *statement;
 
193
    int status, num_bytes;
 
194
    const unsigned char *text;
 
195
    if (db->machine_id != NULL) {
 
196
        *machine_id = db->machine_id;
 
197
        return SQLITE_OK;
 
198
    }
 
199
    status = sqlite3_prepare_v2(db->sql_handle,
 
200
        "SELECT value FROM u1db_config WHERE name = 'machine_id'", -1,
 
201
        &statement, NULL);
 
202
    if(status != SQLITE_OK) {
 
203
        *machine_id = "Failed to prepare statement";
 
204
        return status;
 
205
    }
 
206
    status = sqlite3_step(statement);
 
207
    if(status != SQLITE_ROW) {
 
208
        // TODO: Check return for failures
 
209
        sqlite3_finalize(statement);
 
210
        if (status == SQLITE_DONE) {
 
211
            // No machine_id set yet
 
212
            *machine_id = NULL;
 
213
            return SQLITE_OK;
 
214
        }
 
215
        *machine_id = "Failed to step prepared statement";
 
216
        return status;
 
217
    }
 
218
    if(sqlite3_column_count(statement) != 1) {
 
219
        sqlite3_finalize(statement);
 
220
        *machine_id = "incorrect column count";
 
221
        return status;
 
222
    }
 
223
    text = sqlite3_column_text(statement, 0);
 
224
    num_bytes = sqlite3_column_bytes(statement, 0);
 
225
    db->machine_id = (char *)calloc(1, num_bytes + 1);
 
226
    memcpy(db->machine_id, text, num_bytes+1);
 
227
    *machine_id = db->machine_id;
 
228
    return SQLITE_OK;
 
229
}
 
230
 
 
231
static int
 
232
handle_row(sqlite3_stmt *statement, u1db_row **row)
 
233
{
 
234
    // Note: If this was a performance critical function, we could do a
 
235
    // first-pass over the data and determine total size, and fit all that into
 
236
    // a single calloc call.
 
237
    u1db_row *new_row;
 
238
    const unsigned char *text;
 
239
    int num_bytes, i;
 
240
 
 
241
    new_row = (u1db_row *)calloc(1, sizeof(u1db_row));
 
242
    if (new_row == NULL) {
 
243
        return SQLITE_NOMEM;
 
244
    }
 
245
    if (*row != NULL) {
 
246
        (*row)->next = new_row;
 
247
    }
 
248
    (*row) = new_row;
 
249
    new_row->next = NULL;
 
250
    new_row->num_columns = sqlite3_column_count(statement);
 
251
 
 
252
    new_row->column_sizes = (int*)calloc(new_row->num_columns, sizeof(int));
 
253
    if (new_row->column_sizes == NULL) {
 
254
        return SQLITE_NOMEM;
 
255
    }
 
256
    new_row->columns = (unsigned char**)calloc(new_row->num_columns,
 
257
                                               sizeof(unsigned char *));
 
258
    if (new_row->columns == NULL) {
 
259
        return SQLITE_NOMEM;
 
260
    }
 
261
    for (i = 0; i < new_row->num_columns; i++) {
 
262
        text = sqlite3_column_text(statement, i);
 
263
        // This size does not include the NULL terminator.
 
264
        num_bytes = sqlite3_column_bytes(statement, i);
 
265
        new_row->column_sizes[i] = num_bytes;
 
266
        new_row->columns[i] = (unsigned char*)calloc(num_bytes+1, 1);
 
267
        if (new_row->columns[i] == NULL) {
 
268
            return SQLITE_NOMEM;
 
269
        }
 
270
        memcpy(new_row->columns[i], text, num_bytes+1);
 
271
    }
 
272
    return SQLITE_OK;
 
273
}
 
274
 
 
275
int
 
276
u1db_create_doc(u1database *db, const char *doc, int n, char **doc_id,
 
277
                char **doc_rev)
 
278
{
 
279
    if (db == NULL || doc == NULL || doc_id == NULL || doc_rev == NULL) {
 
280
        // Bad parameter
 
281
        return -1;
 
282
    }
 
283
    if (*doc_id == NULL) {
 
284
        *doc_id = u1db__allocate_doc_id(db);
 
285
    }
 
286
    return u1db_put_doc(db, *doc_id, doc_rev, doc, n);
 
287
}
 
288
 
 
289
 
 
290
// Lookup the contents for doc_id. We return the statement object, since it
 
291
// defines the lifetimes of doc and doc_rev. Callers should then finalize
 
292
// statement when they are done with them. 
 
293
static int
 
294
lookup_doc(u1database *db, const char *doc_id,
 
295
           const unsigned char **doc_rev, const unsigned char **doc, int *n,
 
296
           sqlite3_stmt **statement)
 
297
{
 
298
    int status;
 
299
 
 
300
    status = sqlite3_prepare_v2(db->sql_handle,
 
301
        "SELECT doc_rev, doc FROM document WHERE doc_id = ?", -1,
 
302
        statement, NULL);
 
303
    if (status != SQLITE_OK) {
 
304
        return status;
 
305
    }
 
306
    status = sqlite3_bind_text(*statement, 1, doc_id, -1, SQLITE_TRANSIENT);
 
307
    if (status != SQLITE_OK) {
 
308
        return status;
 
309
    }
 
310
    status = sqlite3_step(*statement);
 
311
    if (status == SQLITE_DONE) {
 
312
        *doc_rev = NULL;
 
313
        *doc = NULL;
 
314
        *n = 0;
 
315
        status = SQLITE_OK;
 
316
    } else if (status == SQLITE_ROW) {
 
317
        *doc_rev = sqlite3_column_text(*statement, 0);
 
318
        // fprintf(stderr, "column_type: %d\n", sqlite3_column_type(*statement, 1));
 
319
        if (sqlite3_column_type(*statement, 1) == SQLITE_NULL) {
 
320
            // fprintf(stderr, "column_type: NULL\n");
 
321
            *doc = NULL;
 
322
            *n = 0;
 
323
        } else {
 
324
            *doc = sqlite3_column_text(*statement, 1);
 
325
            *n = sqlite3_column_bytes(*statement, 1);
 
326
        }
 
327
        status = SQLITE_OK;
 
328
    } else { // Error
 
329
    }
 
330
    return status;
 
331
}
 
332
 
 
333
// Insert the document into the table, we've already done the safety checks
 
334
static int
 
335
write_doc(u1database *db, const char *doc_id, const char *doc_rev,
 
336
          const char *doc, int n, int is_update)
 
337
{
 
338
    sqlite3_stmt *statement;
 
339
    int status;
 
340
 
 
341
    if (is_update) {
 
342
        status = sqlite3_prepare_v2(db->sql_handle, 
 
343
            "UPDATE document SET doc_rev = ?, doc = ? WHERE doc_id = ?", -1,
 
344
            &statement, NULL); 
 
345
    } else {
 
346
        status = sqlite3_prepare_v2(db->sql_handle, 
 
347
            "INSERT INTO document (doc_rev, doc, doc_id) VALUES (?, ?, ?)", -1,
 
348
            &statement, NULL); 
 
349
    }
 
350
    if (status != SQLITE_OK) {
 
351
        return status;
 
352
    }
 
353
    status = sqlite3_bind_text(statement, 1, doc_rev, -1, SQLITE_TRANSIENT);
 
354
    if (status != SQLITE_OK) {
 
355
        sqlite3_finalize(statement);
 
356
        return status;
 
357
    }
 
358
    if (doc == NULL) {
 
359
        status = sqlite3_bind_null(statement, 2);
 
360
    } else {
 
361
        status = sqlite3_bind_text(statement, 2, doc, n, SQLITE_TRANSIENT);
 
362
    }
 
363
    if (status != SQLITE_OK) {
 
364
        sqlite3_finalize(statement);
 
365
        return status;
 
366
    }
 
367
    status = sqlite3_bind_text(statement, 3, doc_id, -1, SQLITE_TRANSIENT);
 
368
    if (status != SQLITE_OK) {
 
369
        sqlite3_finalize(statement);
 
370
        return status;
 
371
    }
 
372
    status = sqlite3_step(statement);
 
373
    if (status == SQLITE_DONE) {
 
374
        status = SQLITE_OK;
 
375
    }
 
376
    sqlite3_finalize(statement);
 
377
    if (status != SQLITE_OK) {
 
378
        return status;
 
379
    }
 
380
    status = sqlite3_prepare_v2(db->sql_handle, 
 
381
        "INSERT INTO transaction_log(doc_id) VALUES (?)", -1,
 
382
        &statement, NULL);
 
383
    if (status != SQLITE_OK) {
 
384
        return status;
 
385
    }
 
386
    status = sqlite3_bind_text(statement, 1, doc_id, -1, SQLITE_TRANSIENT);
 
387
    if (status != SQLITE_OK) {
 
388
        sqlite3_finalize(statement);
 
389
        return status;
 
390
    }
 
391
    status = sqlite3_step(statement);
 
392
    if (status == SQLITE_DONE) {
 
393
        status = SQLITE_OK;
 
394
    }
 
395
    sqlite3_finalize(statement);
 
396
    return status;
 
397
}
 
398
 
 
399
int
 
400
u1db_put_doc(u1database *db, const char *doc_id, char **doc_rev,
 
401
             const char *doc, int n)
 
402
{
 
403
    const unsigned char *old_doc, *old_doc_rev;
 
404
    int status;
 
405
    int old_doc_n;
 
406
    sqlite3_stmt *statement;
 
407
 
 
408
    if (db == NULL || doc == NULL || doc_rev == NULL) {
 
409
        // Bad parameter
 
410
        return -1;
 
411
    }
 
412
    if (doc_id == NULL) {
 
413
        return U1DB_INVALID_DOC_ID;
 
414
    }
 
415
    status = sqlite3_exec(db->sql_handle, "BEGIN", NULL, NULL, NULL);
 
416
    if (status != SQLITE_OK) {
 
417
        return status;
 
418
    }
 
419
    old_doc = NULL;
 
420
    status = lookup_doc(db, doc_id, &old_doc_rev, &old_doc, &old_doc_n, &statement);
 
421
    if (status != SQLITE_OK) {
 
422
        sqlite3_finalize(statement);
 
423
        sqlite3_exec(db->sql_handle, "ROLLBACK", NULL, NULL, NULL);
 
424
        return status;
 
425
    }
 
426
    if (*doc_rev == NULL) {
 
427
        if (old_doc_rev == NULL) {
 
428
            // We are creating a new document from scratch. No problem.
 
429
            status = 0;
 
430
        } else {
 
431
            // We were supplied a NULL doc rev, but the doc already exists
 
432
            status = U1DB_INVALID_DOC_REV;
 
433
        }
 
434
    } else {
 
435
        if (old_doc_rev == NULL) {
 
436
            // TODO: Handle this case, it is probably just
 
437
            //       U1DB_INVALID_DOC_REV, but we want a test case first.
 
438
            // User supplied an old_doc_rev, but there is no entry in the db.
 
439
            status = -12345;
 
440
        } else {
 
441
            if (strcmp(*doc_rev, (const char *)old_doc_rev) == 0) {
 
442
                // The supplied doc_rev exactly matches old_doc_rev, good enough
 
443
                status = 0;
 
444
            } else {
 
445
                // Invalid old rev, mark it as such
 
446
                status = U1DB_INVALID_DOC_REV;
 
447
            }
 
448
        }
 
449
    }
 
450
    if (status == SQLITE_OK) {
 
451
        // We are ok to proceed, allocating a new document revision, and
 
452
        // storing the document
 
453
        u1db_vectorclock *vc;
 
454
        char *machine_id, *new_rev;
 
455
 
 
456
        vc = u1db__vectorclock_from_str((char*)old_doc_rev);
 
457
        if (vc == NULL) { goto finish; }
 
458
        status = u1db_get_machine_id(db, &machine_id);
 
459
        if (status != U1DB_OK) { goto finish; }
 
460
        status = u1db__vectorclock_increment(vc, machine_id);
 
461
        if (status != U1DB_OK) { goto finish; }
 
462
        status = u1db__vectorclock_as_str(vc, &new_rev);
 
463
        if (status != U1DB_OK) { goto finish; }
 
464
        *doc_rev = new_rev;
 
465
        status = write_doc(db, doc_id, new_rev, doc, n, (old_doc != NULL));
 
466
        if (status == SQLITE_OK) {
 
467
            status = sqlite3_exec(db->sql_handle, "COMMIT", NULL, NULL, NULL);
 
468
        }
 
469
    }
 
470
finish:
 
471
    sqlite3_finalize(statement);
 
472
    if (status != SQLITE_OK) {
 
473
        sqlite3_exec(db->sql_handle, "ROLLBACK", NULL, NULL, NULL);
 
474
    }
 
475
    return status;
 
476
}
 
477
 
 
478
int
 
479
u1db_get_doc(u1database *db, const char *doc_id, char **doc_rev,
 
480
             char **doc, int *n, int *has_conflicts)
 
481
{
 
482
    int status = 0, local_n = 0;
 
483
    sqlite3_stmt *statement;
 
484
    const unsigned char *local_doc_rev, *local_doc;
 
485
    if (db == NULL || doc_id == NULL || doc_rev == NULL || doc == NULL || n == NULL
 
486
        || has_conflicts == NULL) {
 
487
        // Bad Parameters
 
488
        // TODO: we could handle has_conflicts == NULL meaning that the caller
 
489
        //       is ignoring conflicts, but we don't want to make it *too* easy
 
490
        //       to do so.
 
491
        return -1;
 
492
    }
 
493
 
 
494
    status = lookup_doc(db, doc_id, &local_doc_rev, &local_doc, &local_n,
 
495
                        &statement);
 
496
    if (status == SQLITE_OK) {
 
497
        if (local_doc_rev == NULL) {
 
498
            *doc_rev = NULL;
 
499
            *doc = NULL;
 
500
            *has_conflicts = 0;
 
501
            goto finish;
 
502
        }
 
503
        if (local_doc == NULL) {
 
504
            *doc = NULL;
 
505
            *n = 0;
 
506
        } else {
 
507
            *doc = (char *)calloc(1, local_n + 1);
 
508
            if (*doc == NULL) {
 
509
                status = SQLITE_NOMEM;
 
510
                goto finish;
 
511
            }
 
512
            memcpy(*doc, local_doc, local_n);
 
513
            *n = local_n;
 
514
        }
 
515
        local_n = strlen((const char*)local_doc_rev);
 
516
        *doc_rev = (char *)calloc(1, local_n+1);
 
517
        if (*doc_rev == NULL) {
 
518
            status = SQLITE_NOMEM;
 
519
            goto finish;
 
520
        }
 
521
        memcpy(*doc_rev, local_doc_rev, local_n);
 
522
        *has_conflicts = 0;
 
523
    } else {
 
524
        *doc_rev = NULL;
 
525
        *doc = NULL;
 
526
        *n = 0;
 
527
        *has_conflicts = 0;
 
528
    }
 
529
finish:
 
530
    sqlite3_finalize(statement);
 
531
    return status;
 
532
}
 
533
 
 
534
int
 
535
u1db_delete_doc(u1database *db, const char *doc_id, char **doc_rev)
 
536
{
 
537
    int status, n;
 
538
    sqlite3_stmt *statement;
 
539
    const unsigned char *cur_doc_rev, *doc;
 
540
 
 
541
    if (db == NULL || doc_id == NULL || doc_rev == NULL || *doc_rev == NULL) {
 
542
        return U1DB_INVALID_PARAMETER;
 
543
    }
 
544
    status = sqlite3_exec(db->sql_handle, "BEGIN", NULL, NULL, NULL);
 
545
    if (status != SQLITE_OK) {
 
546
        return status;
 
547
    }
 
548
    status = lookup_doc(db, doc_id, &cur_doc_rev, &doc, &n, &statement);
 
549
    if (status != SQLITE_OK) {
 
550
        sqlite3_finalize(statement);
 
551
        sqlite3_exec(db->sql_handle, "ROLLBACK", NULL, NULL, NULL);
 
552
        return status;
 
553
    }
 
554
    if (cur_doc_rev == NULL || doc == NULL) {
 
555
        // Can't delete a doc that doesn't exist
 
556
        sqlite3_exec(db->sql_handle, "ROLLBACK", NULL, NULL, NULL);
 
557
        sqlite3_finalize(statement);
 
558
        return U1DB_INVALID_DOC_ID;
 
559
    }
 
560
    if (strcmp((const char *)cur_doc_rev, *doc_rev) != 0) {
 
561
        // The saved document revision doesn't match
 
562
        sqlite3_exec(db->sql_handle, "ROLLBACK", NULL, NULL, NULL);
 
563
        sqlite3_finalize(statement);
 
564
        return U1DB_INVALID_DOC_REV;
 
565
    }
 
566
    // TODO: Handle conflicts
 
567
    sqlite3_finalize(statement);
 
568
 
 
569
    // TODO: Implement VectorClockRev
 
570
    *doc_rev = (char *)calloc(1, 128);
 
571
    memcpy(*doc_rev, "test:2", 6);
 
572
    status = write_doc(db, doc_id, *doc_rev, NULL, 0, 1);
 
573
    if (status != SQLITE_OK) {
 
574
        sqlite3_exec(db->sql_handle, "ROLLBACK", NULL, NULL, NULL);
 
575
    } else {
 
576
        status = sqlite3_exec(db->sql_handle, "COMMIT", NULL, NULL, NULL);
 
577
    }
 
578
    return status;
 
579
}
 
580
 
 
581
int
 
582
u1db_whats_changed(u1database *db, int *db_rev,
 
583
                   int (*cb)(void *, char *doc_id), void *context)
 
584
{
 
585
    int status;
 
586
    sqlite3_stmt *statement;
 
587
    if (db == NULL || db_rev == NULL || cb == NULL) {
 
588
        return -1; // Bad parameters
 
589
    }
 
590
    status = sqlite3_prepare_v2(db->sql_handle,
 
591
        "SELECT db_rev, doc_id FROM transaction_log WHERE db_rev > ?", -1,
 
592
        &statement, NULL);
 
593
    if (status != SQLITE_OK) {
 
594
        return status;
 
595
    }
 
596
    status = sqlite3_bind_int(statement, 1, *db_rev);
 
597
    if (status != SQLITE_OK) {
 
598
        sqlite3_finalize(statement);
 
599
        return status;
 
600
    }
 
601
    status = sqlite3_step(statement);
 
602
    while (status == SQLITE_ROW) {
 
603
        int local_db_rev;
 
604
        char *doc_id;
 
605
        local_db_rev = sqlite3_column_int(statement, 0);
 
606
        if (local_db_rev > *db_rev) {
 
607
            *db_rev = local_db_rev;
 
608
        }
 
609
        doc_id = (char *)sqlite3_column_text(statement, 1);
 
610
        cb(context, doc_id);
 
611
        status = sqlite3_step(statement);
 
612
    }
 
613
    if (status == SQLITE_DONE) {
 
614
        status = SQLITE_OK;
 
615
    }
 
616
    sqlite3_finalize(statement);
 
617
    return status;
 
618
}
 
619
 
 
620
 
 
621
int
 
622
u1db__get_db_rev(u1database *db, int *db_rev)
 
623
{
 
624
    int status;
 
625
    sqlite3_stmt *statement;
 
626
    if (db == NULL || db_rev == NULL) {
 
627
        return U1DB_INVALID_PARAMETER;
 
628
    }
 
629
    status = sqlite3_prepare_v2(db->sql_handle,
 
630
        "SELECT max(db_rev) FROM transaction_log", -1,
 
631
        &statement, NULL);
 
632
    if (status != SQLITE_OK) {
 
633
        return status;
 
634
    }
 
635
    status = sqlite3_step(statement);
 
636
    if (status == SQLITE_DONE) {
 
637
        // No records, we are at rev 0
 
638
        status = SQLITE_OK;
 
639
        *db_rev = 0;
 
640
    } else if (status == SQLITE_ROW) {
 
641
        status = SQLITE_OK;
 
642
        *db_rev = sqlite3_column_int(statement, 0);
 
643
    }
 
644
    sqlite3_finalize(statement);
 
645
    return status;
 
646
}
 
647
 
 
648
char *
 
649
u1db__allocate_doc_id(u1database *db)
 
650
{
 
651
    int db_rev, status;
 
652
    char *buf;
 
653
    status = u1db__get_db_rev(db, &db_rev);
 
654
    if(status != U1DB_OK) {
 
655
        // There was an error.
 
656
        return NULL;
 
657
    }
 
658
    buf = (char *)calloc(1, 128);
 
659
    snprintf(buf, 128, "doc-%d", db_rev);
 
660
    return buf;
 
661
}
 
662
 
 
663
u1db_table *
 
664
u1db__sql_run(u1database *db, const char *sql, size_t n)
 
665
{
 
666
    // TODO: This could be simplified *a lot* by using sqlite3_exec
 
667
    int status, do_continue;
 
668
    u1db_table *result = NULL;
 
669
    u1db_row *cur_row = NULL;
 
670
    sqlite3_stmt *statement;
 
671
    result = (u1db_table *)calloc(1, sizeof(u1db_table));
 
672
    if (result == NULL) {
 
673
        return NULL;
 
674
    }
 
675
    status = sqlite3_prepare_v2(db->sql_handle, sql, n, &statement, NULL); 
 
676
    if (status != SQLITE_OK) {
 
677
        result->status = status;
 
678
        return result;
 
679
    }
 
680
    do_continue = 1;
 
681
    while(do_continue) {
 
682
        do_continue = 0;
 
683
        status = sqlite3_step(statement);
 
684
        switch(status) {
 
685
            case SQLITE_DONE:
 
686
                result->status = SQLITE_OK;
 
687
                break;
 
688
            case SQLITE_ROW:
 
689
                {
 
690
                    status = handle_row(statement, &cur_row);
 
691
                    if (result->first_row == NULL) {
 
692
                        result->first_row = cur_row;
 
693
                    }
 
694
                    if (status == SQLITE_OK) {
 
695
                        do_continue = 1;
 
696
                    }
 
697
                }
 
698
                break;
 
699
            default: // Assume it is an error
 
700
                result->status = status;
 
701
                break;
 
702
        }
 
703
    }
 
704
    sqlite3_finalize(statement);
 
705
    return result;
 
706
}
 
707
 
 
708
void
 
709
u1db__free_table(u1db_table **table)
 
710
{
 
711
    u1db_row *cur_row, *old_row;
 
712
    int i;
 
713
    if (table == NULL || (*table) == NULL) {
 
714
        return;
 
715
    }
 
716
    cur_row = (*table)->first_row;
 
717
    while (cur_row != NULL) {
 
718
        old_row = cur_row;
 
719
        cur_row = cur_row->next;
 
720
        free(old_row->column_sizes);
 
721
        old_row->column_sizes = NULL;
 
722
        for (i = 0; i < old_row->num_columns; i++) {
 
723
            free(old_row->columns[i]);
 
724
            old_row->columns[i] = NULL;
 
725
        }
 
726
        free(old_row->columns);
 
727
        old_row->columns = NULL;
 
728
        free(old_row);
 
729
    }
 
730
    (*table)->first_row = NULL;
 
731
    free(*table);
 
732
    *table = NULL;
 
733
}
 
734
 
 
735
int
 
736
u1db__sync_get_machine_info(u1database *db, const char *other_machine_id,
 
737
                            int *other_db_rev, char **my_machine_id,
 
738
                            int *my_db_rev)
 
739
{
 
740
    int status;
 
741
    sqlite3_stmt *statement;
 
742
 
 
743
    if (db == NULL || other_machine_id == NULL || other_db_rev == NULL) {
 
744
        return U1DB_INVALID_PARAMETER;
 
745
    }
 
746
    status = u1db_get_machine_id(db, my_machine_id);
 
747
    if (status != U1DB_OK) {
 
748
        return status;
 
749
    }
 
750
    status = u1db__get_db_rev(db, my_db_rev);
 
751
    if (status != U1DB_OK) {
 
752
        return status;
 
753
    }
 
754
    status = sqlite3_prepare_v2(db->sql_handle,
 
755
        "SELECT known_db_rev FROM sync_log WHERE machine_id = ?", -1,
 
756
        &statement, NULL);
 
757
    if (status != SQLITE_OK) {
 
758
        return status;
 
759
    }
 
760
    status = sqlite3_bind_text(statement, 1, other_machine_id, -1,
 
761
                               SQLITE_TRANSIENT);
 
762
    if (status != SQLITE_OK) {
 
763
        sqlite3_finalize(statement);
 
764
        return status;
 
765
    }
 
766
    status = sqlite3_step(statement);
 
767
    if (status == SQLITE_DONE) {
 
768
        status = SQLITE_OK;
 
769
        *other_db_rev = 0;
 
770
    } else if (status == SQLITE_ROW) {
 
771
        *other_db_rev = sqlite3_column_int(statement, 0);
 
772
        status = SQLITE_OK;
 
773
    }
 
774
    sqlite3_finalize(statement);
 
775
    return status;
 
776
}
 
777
 
 
778
int
 
779
u1db__sync_record_machine_info(u1database *db, const char *machine_id,
 
780
                               int db_rev)
 
781
{
 
782
    int status;
 
783
    sqlite3_stmt *statement;
 
784
    if (db == NULL || machine_id == NULL) {
 
785
        return U1DB_INVALID_PARAMETER;
 
786
    }
 
787
    status = sqlite3_exec(db->sql_handle, "BEGIN", NULL, NULL, NULL);
 
788
    if (status != SQLITE_OK) {
 
789
        return status;
 
790
    }
 
791
    status = sqlite3_prepare_v2(db->sql_handle,
 
792
        "INSERT OR REPLACE INTO sync_log VALUES (?, ?)", -1,
 
793
        &statement, NULL);
 
794
    if (status != SQLITE_OK) {
 
795
        return status;
 
796
    }
 
797
    status = sqlite3_bind_text(statement, 1, machine_id, -1, SQLITE_TRANSIENT);
 
798
    if (status != SQLITE_OK) {
 
799
        sqlite3_finalize(statement);
 
800
        sqlite3_exec(db->sql_handle, "ROLLBACK", NULL, NULL, NULL);
 
801
        return status;
 
802
    }
 
803
    status = sqlite3_bind_int(statement, 2, db_rev);
 
804
    if (status != SQLITE_OK) {
 
805
        sqlite3_finalize(statement);
 
806
        sqlite3_exec(db->sql_handle, "ROLLBACK", NULL, NULL, NULL);
 
807
        return status;
 
808
    }
 
809
    status = sqlite3_step(statement);
 
810
    if (status == SQLITE_DONE) {
 
811
        status = SQLITE_OK;
 
812
    }
 
813
    sqlite3_finalize(statement);
 
814
    return status;
 
815
}
 
816
 
 
817
static int
 
818
compare_and_insert_doc(u1database *db, const char *doc_rev, const char *doc)
 
819
{
 
820
    return U1DB_INVALID_PARAMETER;
 
821
}
 
822
 
 
823
static int
 
824
insert_records(u1database *db, u1db_record *records)
 
825
{
 
826
    if (db == NULL || records == NULL) {
 
827
        return U1DB_INVALID_PARAMETER;
 
828
    }
 
829
    return U1DB_INVALID_PARAMETER;
 
830
}
 
831
 
 
832
int
 
833
u1db__sync_exchange(u1database *db, const char *from_machine_id,
 
834
                    int from_db_rev, int last_known_rev,
 
835
                    u1db_record *from_records, u1db_record **new_records,
 
836
                    u1db_record **conflict_records)
 
837
{
 
838
    if (db == NULL || from_machine_id == NULL || new_records == NULL
 
839
        || conflict_records == NULL) {
 
840
        return U1DB_INVALID_PARAMETER;
 
841
    }
 
842
    return U1DB_INVALID_PARAMETER;
 
843
}
 
844
 
 
845
u1db_record *
 
846
u1db__create_record(const char *doc_id, const char *doc_rev, const char *doc)
 
847
{
 
848
    // TODO: If we wanted, we could allocate one large block, and then point
 
849
    //       the arrays to the right locations therein.
 
850
    u1db_record *record;
 
851
    record = (u1db_record *)calloc(1, sizeof(u1db_record));
 
852
    if (record == NULL) {
 
853
        return NULL;
 
854
    }
 
855
    record->doc_id = strdup(doc_id);
 
856
    record->doc_rev = strdup(doc_rev);
 
857
    if (doc == NULL) {
 
858
        record->doc = NULL;
 
859
    } else {
 
860
        record->doc = strdup(doc);
 
861
    }
 
862
    return record;
 
863
}
 
864
 
 
865
u1db_record *
 
866
u1db__copy_record(u1db_record *src)
 
867
{
 
868
    if (src == NULL) {
 
869
        return NULL;
 
870
    }
 
871
    return u1db__create_record(src->doc_id, src->doc_rev, src->doc);
 
872
}
 
873
 
 
874
void u1db__free_records(u1db_record **record)
 
875
{
 
876
    u1db_record *cur, *last;
 
877
    if (record == NULL || *record == NULL) {
 
878
        return;
 
879
    }
 
880
    cur = *record;
 
881
    while (cur != NULL) {
 
882
        last = cur;
 
883
        cur = cur->next;
 
884
        free(last->doc_id);
 
885
        free(last->doc_rev);
 
886
        if (last->doc != NULL) {
 
887
            free(last->doc);
 
888
        }
 
889
        free(last);
 
890
    }
 
891
    *record = NULL;
 
892
}
 
893
 
 
894
void u1db__free_vectorclock(u1db_vectorclock **clock)
 
895
{
 
896
    int i;
 
897
    char *machine_id;
 
898
    if (clock == NULL || *clock == NULL) {
 
899
        return;
 
900
    }
 
901
    if ((*clock)->items != NULL) {
 
902
        for (i = 0; i < (*clock)->num_items; i++) {
 
903
            machine_id = (*clock)->items[i].machine_id;
 
904
            if (machine_id != NULL) {
 
905
                free(machine_id);
 
906
            }
 
907
        }
 
908
    }
 
909
    free((*clock)->items);
 
910
    free(*clock);
 
911
    *clock = NULL;
 
912
}
 
913
 
 
914
u1db_vectorclock *u1db__vectorclock_from_str(const char *s)
 
915
{
 
916
    u1db_vectorclock *res = NULL;
 
917
    int i;
 
918
    const char *cur, *colon, *pipe, *end;
 
919
    char *last_digit;
 
920
    if (s == NULL) {
 
921
        s = "";
 
922
    }
 
923
    end = s + strlen(s);
 
924
    res = (u1db_vectorclock *)calloc(1, sizeof(u1db_vectorclock));
 
925
    if (res == NULL) {
 
926
        return NULL;
 
927
    }
 
928
    if ((end - s) == 0) {
 
929
        // Empty string, no items
 
930
        res->items = NULL;
 
931
        res->num_items = 0;
 
932
        return res;
 
933
    }
 
934
    // Count the number of '|' symbols, and allocate buffers for it
 
935
    res->num_items = 1;
 
936
    for (cur = s; cur < end; cur++) {
 
937
        if (*cur == '|') {
 
938
            res->num_items += 1;
 
939
        }
 
940
    }
 
941
    res->items = (u1db_vectorclock_item*)calloc(res->num_items,
 
942
                                        sizeof(u1db_vectorclock_item));
 
943
    // Now walk through it again, looking for the machine:count pairs
 
944
    cur = s;
 
945
    for (i = 0; i < res->num_items; i++) {
 
946
        if (cur >= end) {
 
947
            // Ran off the end. Most likely indicates a trailing | that isn't
 
948
            // followed by content.
 
949
            u1db__free_vectorclock(&res);
 
950
            return NULL;
 
951
        }
 
952
        pipe = memchr(cur, '|', end-cur);
 
953
        if (pipe == NULL) {
 
954
            // We assume the rest of the string is what we want
 
955
            pipe = end;
 
956
        }
 
957
        colon = memchr(cur, ':', pipe-cur);
 
958
        if (colon == NULL || (colon - cur) == 0 || (pipe - colon) == 1) {
 
959
            // Either, no colon, no machine_id, or no digits
 
960
            u1db__free_vectorclock(&res);
 
961
            return NULL;
 
962
        }
 
963
        res->items[i].machine_id = strndup(cur, colon-cur);
 
964
        res->items[i].db_rev = strtol(colon+1, &last_digit, 10);
 
965
        if (last_digit != pipe) {
 
966
            u1db__free_vectorclock(&res);
 
967
            return NULL;
 
968
        }
 
969
        cur = pipe + 1;
 
970
    }
 
971
    return res;
 
972
}
 
973
 
 
974
int
 
975
u1db__vectorclock_increment(u1db_vectorclock *clock, const char *machine_id)
 
976
{
 
977
    int i, cmp;
 
978
    u1db_vectorclock_item *new_buf;
 
979
    if (clock == NULL || machine_id == NULL) {
 
980
        return U1DB_INVALID_PARAMETER;
 
981
    }
 
982
    for (i = 0; i < clock->num_items; ++i) {
 
983
        cmp = strcmp(machine_id, clock->items[i].machine_id);
 
984
        if (cmp == 0) {
 
985
            // We found the entry
 
986
            clock->items[i].db_rev++;
 
987
            return U1DB_OK;
 
988
        } else if (cmp < 0) {
 
989
            // machine_id would come right before items[i] if it was present.
 
990
            // So we break, and insert it here
 
991
            break;
 
992
        }
 
993
    }
 
994
    // If we got here, then 'i' points at the location where we want to insert
 
995
    // a new entry.
 
996
    new_buf = (u1db_vectorclock_item*)realloc(clock->items,
 
997
        sizeof(u1db_vectorclock_item) * (clock->num_items + 1));
 
998
    if (new_buf == NULL) {
 
999
        return SQLITE_NOMEM;
 
1000
    }
 
1001
    clock->items = new_buf;
 
1002
    clock->num_items++;
 
1003
    memmove(&clock->items[i + 1], &clock->items[i],
 
1004
            sizeof(u1db_vectorclock_item) * (clock->num_items - i - 1));
 
1005
    clock->items[i].machine_id = strdup(machine_id);
 
1006
    clock->items[i].db_rev = 1;
 
1007
    return U1DB_OK;
 
1008
}
 
1009
 
 
1010
struct inserts_needed {
 
1011
    struct inserts_needed *next;
 
1012
    int other_offset;
 
1013
    int clock_offset;
 
1014
};
 
1015
 
 
1016
void
 
1017
free_inserts(struct inserts_needed **chain)
 
1018
{
 
1019
    struct inserts_needed *cur, *next;
 
1020
    if (chain == NULL || *chain == NULL) {
 
1021
        return;
 
1022
    }
 
1023
    cur = *chain;
 
1024
    while (cur != NULL) {
 
1025
        next = cur->next;
 
1026
        free(cur);
 
1027
        cur = next;
 
1028
    }
 
1029
    *chain = NULL;
 
1030
}
 
1031
 
 
1032
int
 
1033
u1db__vectorclock_maximize(u1db_vectorclock *clock, u1db_vectorclock *other)
 
1034
{
 
1035
    int ci, oi, cmp;
 
1036
    int num_inserts, move_to_end, num_to_move, item_size;
 
1037
    struct inserts_needed *needed = NULL, *next = NULL;
 
1038
    u1db_vectorclock_item *new_buf;
 
1039
 
 
1040
    if (clock == NULL || other == NULL) {
 
1041
        return U1DB_INVALID_PARAMETER;
 
1042
    }
 
1043
    num_inserts = ci = oi = 0;
 
1044
    // First pass, walk both lists, determining what items need to be inserted
 
1045
    while (oi < other->num_items) {
 
1046
        if (ci >= clock->num_items) {
 
1047
            // We have already walked all of clock, so everything in other
 
1048
            // gets appended
 
1049
            next = (struct inserts_needed *)calloc(1, sizeof(struct inserts_needed));
 
1050
            next->next = needed;
 
1051
            needed = next;
 
1052
            // We need the final offset, after everything has been moved.
 
1053
            next->clock_offset = ci + num_inserts;
 
1054
            next->other_offset = oi;
 
1055
            num_inserts++;
 
1056
            oi++;
 
1057
            continue;
 
1058
        }
 
1059
        cmp = strcmp(clock->items[ci].machine_id, other->items[oi].machine_id);
 
1060
        if (cmp == 0) {
 
1061
            // These machines are the same, take the 'max' value:
 
1062
            if (clock->items[ci].db_rev < other->items[oi].db_rev) {
 
1063
                clock->items[ci].db_rev = other->items[oi].db_rev;
 
1064
            }
 
1065
            ci++;
 
1066
            oi++;
 
1067
            continue;
 
1068
        } else if (cmp < 0) {
 
1069
            // clock[ci] comes before other[oi], so step clock
 
1070
            ci++;
 
1071
        } else {
 
1072
            // oi comes before ci, so it needs to be inserted
 
1073
            next = (struct inserts_needed *)calloc(1, sizeof(struct inserts_needed));
 
1074
            next->next = needed;
 
1075
            needed = next;
 
1076
            next->clock_offset = ci + num_inserts;
 
1077
            next->other_offset = oi;
 
1078
            num_inserts++;
 
1079
            oi++;
 
1080
        }
 
1081
    }
 
1082
    if (num_inserts == 0) {
 
1083
        // Nothing more to do
 
1084
        return U1DB_OK;
 
1085
    }
 
1086
    // Now we need to expand the clock array, and start shuffling the data
 
1087
    // around
 
1088
    item_size = sizeof(u1db_vectorclock_item);
 
1089
    new_buf = (u1db_vectorclock_item *)realloc(clock->items,
 
1090
                item_size * (clock->num_items + num_inserts));
 
1091
    if (new_buf == NULL) {
 
1092
        free_inserts(&needed);
 
1093
        return SQLITE_NOMEM;
 
1094
    }
 
1095
    clock->items = new_buf;
 
1096
    clock->num_items += num_inserts;
 
1097
    next = needed;
 
1098
    move_to_end = clock->num_items - 1;
 
1099
    // Imagine we have 3 inserts, into an initial list 5-wide.
 
1100
    // a c e g h, inserting b f i
 
1101
    // Final length is 8,
 
1102
    // i should have ci=7, num_inserts = 3
 
1103
    // f should have ci=4, num_inserts = 2
 
1104
    // b should have ci=1, num_inserts = 1
 
1105
    // First step, we want to move 0 items, and just insert i at the end (7)
 
1106
    // Second step, we want to move g & h from 3 4, to be at 5 6, and then
 
1107
    // insert f into 4
 
1108
    // Third step, we move c & e from 1 2 to 2 3 and insert b at 1
 
1109
    while (next != NULL) {
 
1110
        num_to_move = move_to_end - next->clock_offset;
 
1111
        if (num_to_move > 0) {
 
1112
            memmove(&clock->items[next->clock_offset + 1],
 
1113
                    &clock->items[next->clock_offset - num_inserts + 1],
 
1114
                    item_size * num_to_move);
 
1115
        }
 
1116
        clock->items[next->clock_offset].machine_id = strdup(
 
1117
            other->items[next->other_offset].machine_id);
 
1118
        clock->items[next->clock_offset].db_rev =
 
1119
            other->items[next->other_offset].db_rev;
 
1120
        num_inserts--;
 
1121
        move_to_end = next->clock_offset - 1;
 
1122
        next = next->next;
 
1123
    }
 
1124
    free_inserts(&needed);
 
1125
    return U1DB_OK;
 
1126
}
 
1127
 
 
1128
int
 
1129
u1db__vectorclock_as_str(u1db_vectorclock *clock, char **result)
 
1130
{
 
1131
    int buf_size, i, val, count;
 
1132
    char *cur, *fmt;
 
1133
    // Quick pass, to determine the buffer size:
 
1134
    buf_size = 0;
 
1135
    if (result == NULL) {
 
1136
        return U1DB_INVALID_PARAMETER;
 
1137
    }
 
1138
    if (clock == NULL) {
 
1139
        // Allocate space for the empty string
 
1140
        cur = (char *)calloc(1, 1);
 
1141
        *result = cur;
 
1142
        return U1DB_OK;
 
1143
    }
 
1144
    for (i = 0; i < clock->num_items; i++) {
 
1145
        buf_size += strlen(clock->items[i].machine_id);
 
1146
        buf_size += 2; // ':' and possible '|'
 
1147
        val = clock->items[i].db_rev;
 
1148
        do {
 
1149
            // divide by 8 is close to divide by 10, to get the number of
 
1150
            // binary digits we will need to represent the decimal form
 
1151
            val >>= 3;
 
1152
            buf_size++;
 
1153
        } while (val > 0);
 
1154
    }
 
1155
    cur = (char *)calloc(1, buf_size);
 
1156
    *result = cur;
 
1157
    for (i = 0; i < clock->num_items; i++) {
 
1158
        if (i == 0) {
 
1159
            fmt = "%s:%d";
 
1160
        } else {
 
1161
            fmt = "|%s:%d";
 
1162
        }
 
1163
        count = snprintf(cur, buf_size, fmt, clock->items[i].machine_id,
 
1164
                         clock->items[i].db_rev);
 
1165
        cur += count;
 
1166
        buf_size -= count;
 
1167
    }
 
1168
    return U1DB_OK;
 
1169
}
 
1170
 
 
1171
int
 
1172
u1db__vectorclock_is_newer(u1db_vectorclock *maybe_newer,
 
1173
                           u1db_vectorclock *older)
 
1174
{
 
1175
    int ci, oi, cmp, is_newer, n_db_rev, o_db_rev;
 
1176
    if (maybe_newer == NULL || maybe_newer->num_items == 0) {
 
1177
        // NULL is never newer
 
1178
        return 0;
 
1179
    }
 
1180
    if (older == NULL || older->num_items == 0) {
 
1181
        // This is not NULL, so it should be newer, we may need to check if
 
1182
        // self is the empty string, though.
 
1183
        return 1;
 
1184
    }
 
1185
    ci = oi = 0;
 
1186
    is_newer = 0;
 
1187
    // First pass, walk both lists, determining what items need to be inserted
 
1188
    while (oi < older->num_items && ci < maybe_newer->num_items) {
 
1189
        cmp = strcmp(maybe_newer->items[ci].machine_id,
 
1190
                     older->items[oi].machine_id);
 
1191
        if (cmp == 0) {
 
1192
            // Both clocks have the same machine, see if one is newer
 
1193
            n_db_rev = maybe_newer->items[ci].db_rev;
 
1194
            o_db_rev = older->items[ci].db_rev;
 
1195
            if (n_db_rev < o_db_rev) {
 
1196
                // At least one entry in older is newer than this
 
1197
                return 0;
 
1198
            } else if (n_db_rev > o_db_rev) {
 
1199
                // If we have no conflicts, this is strictly newer
 
1200
                is_newer = 1;
 
1201
            }
 
1202
            ci++;
 
1203
            oi++;
 
1204
            continue;
 
1205
        } else if (cmp < 0) {
 
1206
            // maybe_newer has an entry that older doesn't have, which would
 
1207
            // make it newer
 
1208
            is_newer = 1;
 
1209
            ci++;
 
1210
        } else {
 
1211
            // older has an entry that maybe_newer doesn't have, so we must
 
1212
            // not be strictly newer
 
1213
            return 0;
 
1214
        }
 
1215
    }
 
1216
    if (oi == older->num_items && ci < maybe_newer->num_items) {
 
1217
        // ci has an entry that older doesn't have, it is newer
 
1218
        is_newer = 1;
 
1219
    }
 
1220
    if (oi < older->num_items) {
 
1221
        // We didn't walk all of older, which means it has an entry which ci
 
1222
        // doesn't have, and thus maybe_newer is not strictly newer
 
1223
        return 0;
 
1224
    }
 
1225
    return is_newer;
 
1226
}