sqlite3_accessor.cc 53.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15
16
#include <sqlite3.h>

17
#include <string>
18
#include <utility>
19
20
#include <vector>

21
22
#include <exceptions/exceptions.h>

23
24
#include <dns/name.h>

25
#include <datasrc/sqlite3_accessor.h>
26
27
#include <datasrc/logger.h>
#include <datasrc/data_source.h>
28
#include <datasrc/factory.h>
Stephen Morris's avatar
Stephen Morris committed
29
#include <datasrc/database.h>
30
#include <util/filename.h>
31

32
using namespace std;
33
using namespace isc::data;
34

35
36
37
38
39
40
41
42
43
44
45
46
namespace {
// Expected schema.  The major version must match else there is an error.  If
// the minor version of the database is less than this, a warning is output.
//
// It is assumed that a program written to run on m.n of the database will run
// with a database version m.p, where p is any number.  However, if p < n,
// we assume that the database structure was upgraded for some reason, and that
// some advantage may result if the database is upgraded. Conversely, if p > n,
// The database is at a later version than the program was written for and the
// program may not be taking advantage of features (possibly performance
// improvements) added to the database.
const int SQLITE_SCHEMA_MAJOR_VERSION = 2;
47
const int SQLITE_SCHEMA_MINOR_VERSION = 2;
48
}
49

50
51
52
namespace isc {
namespace datasrc {

53
54
55
56
57
58
59
60
// The following enum and char* array define the SQL statements commonly
// used in this implementation.  Corresponding prepared statements (of
// type sqlite3_stmt*) are maintained in the statements_ array of the
// SQLite3Parameters structure.

enum StatementID {
    ZONE = 0,
    ANY = 1,
61
62
63
64
65
66
67
    ANY_SUB = 2,
    BEGIN = 3,
    COMMIT = 4,
    ROLLBACK = 5,
    DEL_ZONE_RECORDS = 6,
    ADD_RECORD = 7,
    DEL_RECORD = 8,
68
69
70
71
72
73
74
75
76
77
78
79
80
    ITERATE_RECORDS = 9,
    ITERATE_NSEC3 = 10,
    FIND_PREVIOUS = 11,
    ADD_RECORD_DIFF = 12,
    LOW_DIFF_ID = 13,
    HIGH_DIFF_ID = 14,
    DIFF_RECS = 15,
    NSEC3 = 16,
    NSEC3_PREVIOUS = 17,
    NSEC3_LAST = 18,
    ADD_NSEC3_RECORD = 19,
    DEL_ZONE_NSEC3_RECORDS = 20,
    DEL_NSEC3_RECORD = 21,
81
    ADD_ZONE = 22,
82
83
    DELETE_ZONE = 23,
    NUM_STATEMENTS = 24
84
85
86
};

const char* const text_statements[NUM_STATEMENTS] = {
87
88
    // note for ANY and ITERATE: the order of the SELECT values is
    // specifically chosen to match the enum values in RecordColumns
89
90
    "SELECT id FROM zones WHERE name=?1 AND rdclass = ?2", // ZONE
    "SELECT rdtype, ttl, sigtype, rdata FROM records "     // ANY
91
        "WHERE zone_id=?1 AND name=?2",
92
93
94
95
96

    // ANY_SUB:
    // This query returns records in the specified zone for the domain
    // matching the passed name, and its sub-domains.
    "SELECT rdtype, ttl, sigtype, rdata "
97
        "FROM records WHERE zone_id=?1 AND rname LIKE ?2",
98

99
100
101
102
103
    "BEGIN",                    // BEGIN
    "COMMIT",                   // COMMIT
    "ROLLBACK",                 // ROLLBACK
    "DELETE FROM records WHERE zone_id=?1", // DEL_ZONE_RECORDS
    "INSERT INTO records "      // ADD_RECORD
104
105
        "(zone_id, name, rname, ttl, rdtype, sigtype, rdata) "
        "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
106
    "DELETE FROM records WHERE zone_id=?1 AND name=?2 " // DEL_RECORD
107
        "AND rdtype=?3 AND rdata=?4",
108
109
110
111
112
113
114
115

    // ITERATE_RECORDS:
    // The following iterates the whole zone in the records table.
    "SELECT rdtype, ttl, sigtype, rdata, name FROM records "
        "WHERE zone_id = ?1 ORDER BY rname, rdtype",

    // ITERATE_NSEC3:
    // The following iterates the whole zone in the nsec3 table. As the
116
    // RRSIGs are for NSEC3s, we can hardcode the sigtype.
117
    "SELECT rdtype, ttl, \"NSEC3\", rdata, owner FROM nsec3 "
118
        "WHERE zone_id = ?1 ORDER BY hash, rdtype",
119
    /*
120
121
122
     * This one looks for previous name with NSEC record. It is done by
     * using the reversed name. The NSEC is checked because we need to
     * skip glue data, which don't have the NSEC.
123
     */
124
    "SELECT name FROM records " // FIND_PREVIOUS
125
        "WHERE zone_id=?1 AND rdtype = 'NSEC' AND "
126
        "rname < ?2 ORDER BY rname DESC LIMIT 1",
127
    "INSERT INTO diffs "        // ADD_RECORD_DIFF
128
129
130
        "(zone_id, version, operation, name, rrtype, ttl, rdata) "
        "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",

131
132
133
    // Two statements to select the lowest ID and highest ID in a set of
    // differences.
    "SELECT id FROM diffs "     // LOW_DIFF_ID
Stephen Morris's avatar
Stephen Morris committed
134
        "WHERE zone_id=?1 AND version=?2 and OPERATION=?3 "
135
        "ORDER BY id ASC LIMIT 1",
136
    "SELECT id FROM diffs "     // HIGH_DIFF_ID
Stephen Morris's avatar
Stephen Morris committed
137
        "WHERE zone_id=?1 AND version=?2 and OPERATION=?3 "
138
        "ORDER BY id DESC LIMIT 1",
139
140
141
142

    // In the next statement, note the redundant ID.  This is to ensure
    // that the columns match the column IDs passed to the iterator
    "SELECT rrtype, ttl, id, rdata, name FROM diffs "   // DIFF_RECS
Stephen Morris's avatar
Stephen Morris committed
143
        "WHERE zone_id=?1 AND id>=?2 and id<=?3 "
144
145
        "ORDER BY id ASC",

146
    // NSEC3: Query to get the NSEC3 records
147
148
149
150
151
    //
    // The "1" in SELECT is for positioning the rdata column to the
    // expected position, so we can reuse the same code as for other
    // lookups.
    "SELECT rdtype, ttl, 1, rdata FROM nsec3 WHERE zone_id=?1 AND "
152
        "hash=?2",
153
    // NSEC3_PREVIOUS: For getting the previous NSEC3 hash
154
155
    "SELECT DISTINCT hash FROM nsec3 WHERE zone_id=?1 AND hash < ?2 "
        "ORDER BY hash DESC LIMIT 1",
156
    // NSEC3_LAST: And for wrap-around
157
158
    "SELECT DISTINCT hash FROM nsec3 WHERE zone_id=?1 "
        "ORDER BY hash DESC LIMIT 1",
159
160
    // ADD_NSEC3_RECORD: Add NSEC3-related (NSEC3 or NSEC3-covering RRSIG) RR
    "INSERT INTO nsec3 (zone_id, hash, owner, ttl, rdtype, rdata) "
161
    "VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
162
    // DEL_ZONE_NSEC3_RECORDS: delete all NSEC3-related records from the zone
163
164
165
    "DELETE FROM nsec3 WHERE zone_id=?1",
    // DEL_NSEC3_RECORD: delete specified NSEC3-related records
    "DELETE FROM nsec3 WHERE zone_id=?1 AND hash=?2 "
166
167
168
    "AND rdtype=?3 AND rdata=?4",

    // ADD_ZONE: add a zone to the zones table
169
170
171
    "INSERT INTO zones (name, rdclass) VALUES (?1, ?2)", // ADD_ZONE
    // DELETE_ZONE: delete a zone from the zones table
    "DELETE FROM zones WHERE id=?1" // DELETE_ZONE
172
173
};

174
175
struct SQLite3Parameters {
    SQLite3Parameters() :
176
177
        db_(NULL), major_version_(-1), minor_version_(-1),
        in_transaction(false), updating_zone(false), updated_zone_id(-1)
178
179
180
181
182
    {
        for (int i = 0; i < NUM_STATEMENTS; ++i) {
            statements_[i] = NULL;
        }
    }
183

184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
    // This method returns the specified ID of SQLITE3 statement.  If it's
    // not yet prepared it internally creates a new one.  This way we can
    // avoid preparing unnecessary statements and minimize the overhead.
    sqlite3_stmt*
    getStatement(int id) {
        assert(id < NUM_STATEMENTS);
        if (statements_[id] == NULL) {
            assert(db_ != NULL);
            sqlite3_stmt* prepared = NULL;
            if (sqlite3_prepare_v2(db_, text_statements[id], -1, &prepared,
                                   NULL) != SQLITE_OK) {
                isc_throw(SQLite3Error, "Could not prepare SQLite statement: "
                          << text_statements[id] <<
                          ": " << sqlite3_errmsg(db_));
            }
            statements_[id] = prepared;
        }
        return (statements_[id]);
    }

    void
    finalizeStatements() {
        for (int i = 0; i < NUM_STATEMENTS; ++i) {
207
208
209
210
            if (statements_[i] != NULL) {
                sqlite3_finalize(statements_[i]);
                statements_[i] = NULL;
            }
211
212
213
        }
    }

214
    sqlite3* db_;
215
216
    int major_version_;
    int minor_version_;
217
218
219
    bool in_transaction; // whether or not a transaction has been started
    bool updating_zone;          // whether or not updating the zone
    int updated_zone_id;        // valid only when in_transaction is true
220
    string updated_zone_origin_; // ditto, and only needed to handle NSEC3s
221
222
223
224
private:
    // statements_ are private and must be accessed via getStatement() outside
    // of this structure.
    sqlite3_stmt* statements_[NUM_STATEMENTS];
225
226
227
228
229
230
231
232
233
234
};

// This is a helper class to encapsulate the code logic of executing
// a specific SQLite3 statement, ensuring the corresponding prepared
// statement is always reset whether the execution is completed successfully
// or it results in an exception.
// Note that an object of this class is intended to be used for "ephemeral"
// statement, which is completed with a single "step" (normally within a
// single call to an SQLite3Database method).  In particular, it cannot be
// used for "SELECT" variants, which generally expect multiple matching rows.
235
236
237
238
//
// The bindXXX methods are straightforward wrappers for the corresponding
// sqlite3_bind_xxx functions that make bindings with the given parameters
// on the statement maintained in this class.
239
class StatementProcessor {
240
241
242
public:
    // desc will be used on failure in the what() message of the resulting
    // DataSourceError exception.
243
244
    StatementProcessor(SQLite3Parameters& dbparameters, StatementID stmt_id,
                       const char* desc) :
245
246
        dbparameters_(dbparameters), stmt_(dbparameters.getStatement(stmt_id)),
        desc_(desc)
247
    {
248
        sqlite3_clear_bindings(stmt_);
249
250
    }

251
    ~StatementProcessor() {
252
        sqlite3_reset(stmt_);
253
254
    }

255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
    void bindInt(int index, int val) {
        if (sqlite3_bind_int(stmt_, index, val) != SQLITE_OK) {
            isc_throw(DataSourceError,
                      "failed to bind SQLite3 parameter: " <<
                      sqlite3_errmsg(dbparameters_.db_));
        }
    }

    void bindInt64(int index, sqlite3_int64 val) {
        if (sqlite3_bind_int64(stmt_, index, val) != SQLITE_OK) {
            isc_throw(DataSourceError,
                      "failed to bind SQLite3 parameter: " <<
                      sqlite3_errmsg(dbparameters_.db_));
        }
    }

    // For simplicity, we assume val is a NULL-terminated string, and the
    // entire non NUL characters are to be bound.  The destructor parameter
    // is normally either SQLITE_TRANSIENT or SQLITE_STATIC.
    void bindText(int index, const char* val, void(*destructor)(void*)) {
        if (sqlite3_bind_text(stmt_, index, val, -1, destructor)
            != SQLITE_OK) {
            isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
                      sqlite3_errmsg(dbparameters_.db_));
        }
    }

282
    void exec() {
283
284
        if (sqlite3_step(stmt_) != SQLITE_DONE) {
            sqlite3_reset(stmt_);
285
            isc_throw(DataSourceError, "failed to " << desc_ << ": " <<
286
                      sqlite3_errmsg(dbparameters_.db_));
287
288
289
290
        }
    }

private:
291
    SQLite3Parameters& dbparameters_;
292
    sqlite3_stmt* stmt_;
293
    const char* const desc_;
294
295
};

296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
SQLite3Accessor::SQLite3Accessor(const std::string& filename,
                                 const string& rrclass) :
    dbparameters_(new SQLite3Parameters),
    filename_(filename),
    class_(rrclass),
    database_name_("sqlite3_" +
                   isc::util::Filename(filename).nameAndExtension())
{
    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_NEWCONN);

    open(filename);
}

boost::shared_ptr<DatabaseAccessor>
SQLite3Accessor::clone() {
    return (boost::shared_ptr<DatabaseAccessor>(new SQLite3Accessor(filename_,
                                                                    class_)));
}

315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
namespace {

// This is a helper class to initialize a Sqlite3 DB safely.  An object of
// this class encapsulates all temporary resources that are necessary for
// the initialization, and release them in the destructor.  Once everything
// is properly initialized, the move() method moves the allocated resources
// to the main object in an exception free manner.  This way, the main code
// for the initialization can be exception safe, and can provide the strong
// exception guarantee.
class Initializer {
public:
    ~Initializer() {
        if (params_.db_ != NULL) {
            sqlite3_close(params_.db_);
        }
    }
    void move(SQLite3Parameters* dst) {
        *dst = params_;
        params_ = SQLite3Parameters(); // clear everything
    }
    SQLite3Parameters params_;
};

const char* const SCHEMA_LIST[] = {
339
340
    "CREATE TABLE schema_version (version INTEGER NOT NULL, "
        "minor INTEGER NOT NULL DEFAULT 0)",
341
    "INSERT INTO schema_version VALUES (2, 2)",
342
    "CREATE TABLE zones (id INTEGER PRIMARY KEY, "
343
344
    "name TEXT NOT NULL COLLATE NOCASE, "
    "rdclass TEXT NOT NULL COLLATE NOCASE DEFAULT 'IN', "
345
346
347
    "dnssec BOOLEAN NOT NULL DEFAULT 0)",
    "CREATE INDEX zones_byname ON zones (name)",
    "CREATE TABLE records (id INTEGER PRIMARY KEY, "
348
349
350
351
        "zone_id INTEGER NOT NULL, name TEXT NOT NULL COLLATE NOCASE, "
        "rname TEXT NOT NULL COLLATE NOCASE, ttl INTEGER NOT NULL, "
        "rdtype TEXT NOT NULL COLLATE NOCASE, sigtype TEXT COLLATE NOCASE, "
        "rdata TEXT NOT NULL)",
352
353
    "CREATE INDEX records_byname ON records (name)",
    "CREATE INDEX records_byrname ON records (rname)",
354
355
356
357
358
359
360
    // The next index is a tricky one.  It's necessary for
    // FIND_PREVIOUS to use the index efficiently; since there's an
    // "inequality", the rname column must be placed later.  records_byrname
    // may not be sufficient especially when the zone is not signed (and
    // defining a separate index for rdtype only doesn't work either; SQLite3
    // would then create a temporary B-tree for "ORDER BY").
    "CREATE INDEX records_bytype_and_rname ON records (rdtype, rname)",
361
    "CREATE INDEX records_byrname_and_rdtype ON records (rname, rdtype)",
362
    "CREATE TABLE nsec3 (id INTEGER PRIMARY KEY, zone_id INTEGER NOT NULL, "
363
364
365
366
        "hash TEXT NOT NULL COLLATE NOCASE, "
        "owner TEXT NOT NULL COLLATE NOCASE, "
        "ttl INTEGER NOT NULL, rdtype TEXT NOT NULL COLLATE NOCASE, "
        "rdata TEXT NOT NULL)",
367
    "CREATE INDEX nsec3_byhash ON nsec3 (hash)",
368
    "CREATE INDEX nsec3_byhash_and_rdtype ON nsec3 (hash, rdtype)",
369
    "CREATE TABLE diffs (id INTEGER PRIMARY KEY, "
370
371
372
        "zone_id INTEGER NOT NULL, "
        "version INTEGER NOT NULL, "
        "operation INTEGER NOT NULL, "
373
374
        "name TEXT NOT NULL COLLATE NOCASE, "
        "rrtype TEXT NOT NULL COLLATE NOCASE, "
375
        "ttl INTEGER NOT NULL, "
376
        "rdata TEXT NOT NULL)",
377
378
379
380
381
382
383
384
    NULL
};

sqlite3_stmt*
prepare(sqlite3* const db, const char* const statement) {
    sqlite3_stmt* prepared = NULL;
    if (sqlite3_prepare_v2(db, statement, -1, &prepared, NULL) != SQLITE_OK) {
        isc_throw(SQLite3Error, "Could not prepare SQLite statement: " <<
385
                  statement << ": " << sqlite3_errmsg(db));
386
387
388
389
    }
    return (prepared);
}

390
391
392
// small function to sleep for 0.1 seconds, needed when waiting for
// exclusive database locks (which should only occur on startup, and only
// when the database has not been created yet)
393
void doSleep() {
394
395
396
397
398
    struct timespec req;
    req.tv_sec = 0;
    req.tv_nsec = 100000000;
    nanosleep(&req, NULL);
}
399

400
401
// returns the schema version if the schema version table exists
// returns -1 if it does not
402
int checkSchemaVersionElement(sqlite3* db, const char* const query) {
403
    sqlite3_stmt* prepared = NULL;
404
405
406
407
    // At this point in time, the database might be exclusively locked, in
    // which case even prepare() will return BUSY, so we may need to try a
    // few times
    for (size_t i = 0; i < 50; ++i) {
408
        int rc = sqlite3_prepare_v2(db, query, -1, &prepared, NULL);
409
410
411
        if (rc == SQLITE_ERROR) {
            // this is the error that is returned when the table does not
            // exist
412
            sqlite3_finalize(prepared);
413
414
415
416
            return (-1);
        } else if (rc == SQLITE_OK) {
            break;
        } else if (rc != SQLITE_BUSY || i == 50) {
417
            sqlite3_finalize(prepared);
418
419
            isc_throw(SQLite3Error, "Unable to prepare version query: "
                        << rc << " " << sqlite3_errmsg(db));
420
        }
421
        doSleep();
422
423
    }
    if (sqlite3_step(prepared) != SQLITE_ROW) {
424
        sqlite3_finalize(prepared);
425
426
427
428
429
430
431
432
        isc_throw(SQLite3Error,
                    "Unable to query version: " << sqlite3_errmsg(db));
    }
    int version = sqlite3_column_int(prepared, 0);
    sqlite3_finalize(prepared);
    return (version);
}

433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
// Returns the schema major and minor version numbers in a pair.
// Returns (-1, -1) if the table does not exist, (1, 0) for a V1
// database, and (n, m) for any other.
pair<int, int> checkSchemaVersion(sqlite3* db) {
    int major = checkSchemaVersionElement(db,
        "SELECT version FROM schema_version");
    if (major == -1) {
        return (make_pair(-1, -1));
    } else if (major == 1) {
        return (make_pair(1, 0));
    } else {
        int minor = checkSchemaVersionElement(db,
            "SELECT minor FROM schema_version");
        return (make_pair(major, minor));
    }
}

450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
// A helper class used in createDatabase() below so we manage the one shot
// transaction safely.
class ScopedTransaction {
public:
    ScopedTransaction(sqlite3* db) : db_(NULL) {
        // try for 5 secs (50*0.1)
        for (size_t i = 0; i < 50; ++i) {
            const int rc = sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION",
                                        NULL, NULL, NULL);
            if (rc == SQLITE_OK) {
                break;
            } else if (rc != SQLITE_BUSY || i == 50) {
                isc_throw(SQLite3Error, "Unable to acquire exclusive lock "
                          "for database creation: " << sqlite3_errmsg(db));
            }
            doSleep();
        }
        // Hold the DB pointer once we have successfully acquired the lock.
        db_ = db;
    }
    ~ScopedTransaction() {
        if (db_ != NULL) {
            // Note: even rollback could fail in theory, but in that case
            // we cannot do much for safe recovery anyway.  We could at least
            // log the event, but for now don't even bother to do that, with
            // the expectation that we'll soon stop creating the schema in this
            // module.
            sqlite3_exec(db_, "ROLLBACK", NULL, NULL, NULL);
        }
    }
    void commit() {
        if (sqlite3_exec(db_, "COMMIT TRANSACTION", NULL, NULL, NULL) !=
            SQLITE_OK) {
            isc_throw(SQLite3Error, "Unable to commit newly created database "
                      "schema: " << sqlite3_errmsg(db_));
        }
        db_ = NULL;
    }

private:
    sqlite3* db_;
};

493
// return db version
494
pair<int, int>
495
496
createDatabase(sqlite3* db, const std::string& name) {
    logger.warn(DATASRC_SQLITE_SETUP).arg(name);
497

498
499
    // try to get an exclusive lock. Once that is obtained, do the version
    // check *again*, just in case this process was racing another
500
    ScopedTransaction trasaction(db);
501
502
    pair<int, int> schema_version = checkSchemaVersion(db);
    if (schema_version.first == -1) {
503
504
505
506
        for (int i = 0; SCHEMA_LIST[i] != NULL; ++i) {
            if (sqlite3_exec(db, SCHEMA_LIST[i], NULL, NULL, NULL) !=
                SQLITE_OK) {
                isc_throw(SQLite3Error,
507
                          "Failed to set up schema " << SCHEMA_LIST[i]);
508
509
            }
        }
510
        trasaction.commit();
511
512
513
514
515

        // Return the version.  We query again to ensure that the only point
        // in which the current schema version is defined is in the create
        // statements.
        schema_version = checkSchemaVersion(db);
516
    }
517
518

    return (schema_version);
519
520
521
}

void
522
checkAndSetupSchema(Initializer* initializer, const std::string& name) {
523
524
    sqlite3* const db = initializer->params_.db_;

525
526
    pair<int, int> schema_version = checkSchemaVersion(db);
    if (schema_version.first == -1) {
527
        schema_version = createDatabase(db, name);
528
529
530
531
    } else if (schema_version.first != SQLITE_SCHEMA_MAJOR_VERSION) {
        LOG_ERROR(logger, DATASRC_SQLITE_INCOMPATIBLE_VERSION)
            .arg(schema_version.first).arg(schema_version.second)
            .arg(SQLITE_SCHEMA_MAJOR_VERSION).arg(SQLITE_SCHEMA_MINOR_VERSION);
532
533
534
        isc_throw(IncompatibleDbVersion,
                  "incompatible SQLite3 database version: " <<
                  schema_version.first << "." << schema_version.second);
535
536
537
538
    } else if (schema_version.second < SQLITE_SCHEMA_MINOR_VERSION) {
        LOG_WARN(logger, DATASRC_SQLITE_COMPATIBLE_VERSION)
            .arg(schema_version.first).arg(schema_version.second)
            .arg(SQLITE_SCHEMA_MAJOR_VERSION).arg(SQLITE_SCHEMA_MINOR_VERSION);
539
    }
540

541
542
    initializer->params_.major_version_ = schema_version.first;
    initializer->params_.minor_version_ = schema_version.second;
543
544
545
546
547
}

}

void
548
SQLite3Accessor::open(const std::string& name) {
549
550
551
552
553
554
555
556
557
558
559
560
    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CONNOPEN).arg(name);
    if (dbparameters_->db_ != NULL) {
        // There shouldn't be a way to trigger this anyway
        isc_throw(DataSourceError, "Duplicate SQLite open with " << name);
    }

    Initializer initializer;

    if (sqlite3_open(name.c_str(), &initializer.params_.db_) != 0) {
        isc_throw(SQLite3Error, "Cannot open SQLite database file: " << name);
    }

561
    checkAndSetupSchema(&initializer, name);
562
    initializer.move(dbparameters_.get());
563
564
}

565
SQLite3Accessor::~SQLite3Accessor() {
566
567
568
569
570
571
572
    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_DROPCONN);
    if (dbparameters_->db_ != NULL) {
        close();
    }
}

void
573
SQLite3Accessor::close(void) {
574
575
576
577
578
579
    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CONNCLOSE);
    if (dbparameters_->db_ == NULL) {
        isc_throw(DataSourceError,
                  "SQLite data source is being closed before open");
    }

580
    dbparameters_->finalizeStatements();
581
582
583
584
585
    sqlite3_close(dbparameters_->db_);
    dbparameters_->db_ = NULL;
}

std::pair<bool, int>
586
SQLite3Accessor::getZone(const std::string& name) const {
587
    int rc;
588
    sqlite3_stmt* const stmt = dbparameters_->getStatement(ZONE);
589

590
591
    // Take the statement (simple SELECT id FROM zones WHERE...)
    // and prepare it (bind the parameters to it)
592
593
    sqlite3_reset(stmt);
    rc = sqlite3_bind_text(stmt, 1, name.c_str(), -1, SQLITE_STATIC);
594
595
596
597
    if (rc != SQLITE_OK) {
        isc_throw(SQLite3Error, "Could not bind " << name <<
                  " to SQL statement (zone)");
    }
598
    rc = sqlite3_bind_text(stmt, 2, class_.c_str(), -1, SQLITE_STATIC);
599
600
601
602
603
    if (rc != SQLITE_OK) {
        isc_throw(SQLite3Error, "Could not bind " << class_ <<
                  " to SQL statement (zone)");
    }

604
    // Get the data there and see if it found anything
605
    rc = sqlite3_step(stmt);
606
    if (rc == SQLITE_ROW) {
607
608
609
        const int zone_id = sqlite3_column_int(stmt, 0);
        sqlite3_reset(stmt);
        return (pair<bool, int>(true, zone_id));
610
611
    } else if (rc == SQLITE_DONE) {
        // Free resources
612
613
        sqlite3_reset(stmt);
        return (pair<bool, int>(false, 0));
614
    }
615

616
    sqlite3_reset(stmt);
617
    isc_throw(DataSourceError, "Unexpected failure in sqlite3_step: " <<
618
              sqlite3_errmsg(dbparameters_->db_));
619
620
    // Compilers might not realize isc_throw always throws
    return (std::pair<bool, int>(false, 0));
621
622
}

623
int
624
SQLite3Accessor::addZone(const std::string& name) {
625
626
627
628
629
630
631
    // Transaction should have been started by the caller
    if (!dbparameters_->in_transaction) {
        isc_throw(DataSourceError, "performing addZone on SQLite3 "
                  "data source without transaction");
    }

    StatementProcessor proc(*dbparameters_, ADD_ZONE, "add zone");
632
633
634
    // note: using TRANSIENT here, STATIC would be slightly more
    // efficient, but TRANSIENT is safer and performance is not
    // as important in this specific code path.
635
    proc.bindText(1, name.c_str(), SQLITE_TRANSIENT);
636
637
638
639
    proc.bindText(2, class_.c_str(), SQLITE_TRANSIENT);
    proc.exec();

    // There are tricks to getting this in one go, but it is safer
640
641
642
643
644
645
    // to do a new lookup (sqlite3_last_insert_rowid is unsafe
    // regarding threads and triggers). This requires two
    // statements, and is unpredictable in the case a zone is added
    // twice, but this method assumes the caller does not do that
    // anyway
    std::pair<bool, int> getzone_result = getZone(name);
646
647
648
649
    assert(getzone_result.first);
    return (getzone_result.second);
}

650
651
652
653
654
655
656
657
658
659
660
661
662
void
SQLite3Accessor::deleteZone(int zone_id) {
    // Transaction should have been started by the caller
    if (!dbparameters_->in_transaction) {
        isc_throw(InvalidOperation, "performing deleteZone on SQLite3 "
                  "data source without transaction");
    }

    StatementProcessor proc(*dbparameters_, DELETE_ZONE, "delete zone");
    proc.bindInt(1, zone_id);
    proc.exec();
}

663
664
665
666
namespace {

// Conversion to plain char
const char*
667
convertToPlainChar(const unsigned char* ucp, sqlite3 *db) {
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
    if (ucp == NULL) {
        // The field can really be NULL, in which case we return an
        // empty string, or sqlite may have run out of memory, in
        // which case we raise an error
        if (sqlite3_errcode(db) == SQLITE_NOMEM) {
            isc_throw(DataSourceError,
                      "Sqlite3 backend encountered a memory allocation "
                      "error in sqlite3_column_text()");
        } else {
            return ("");
        }
    }
    const void* p = ucp;
    return (static_cast<const char*>(p));
}

}
685
686

// cppcheck-suppress noConstructor
687
class SQLite3Accessor::Context : public DatabaseAccessor::IteratorContext {
688
public:
689
690
    // Construct an iterator for all records. When constructed this
    // way, the getNext() call will copy all fields
691
    Context(const boost::shared_ptr<const SQLite3Accessor>& accessor, int id) :
692
        iterator_type_(ITT_ALL),
693
        accessor_(accessor),
694
        statement_(NULL),
695
696
697
        statement2_(NULL),
        rc_(SQLITE_OK),
        rc2_(SQLITE_OK),
698
        name_("")
699
    {
700
701
702
703
704
705
706
707
        // We create the statements now and then just keep getting data
        // from them.
        statement_ = prepare(accessor->dbparameters_->db_,
                             text_statements[ITERATE_NSEC3]);
        bindZoneId(id);

        std::swap(statement_, statement2_);

708
        statement_ = prepare(accessor->dbparameters_->db_,
709
                             text_statements[ITERATE_RECORDS]);
Jelte Jansen's avatar
Jelte Jansen committed
710
        bindZoneId(id);
711
    }
712

713
714
715
716
717
718
719
720
    // What kind of query it is - selection of the statement for DB
    enum QueryType {
        QT_ANY, // Directly for a domain
        QT_SUBDOMAINS, // Subdomains of a given domain
        QT_NSEC3 // Domain in the NSEC3 namespace (the name is is the hash,
                 // not the whole name)
    };

721
722
    // Construct an iterator for records with a specific name. When constructed
    // this way, the getNext() call will copy all fields except name
723
    Context(const boost::shared_ptr<const SQLite3Accessor>& accessor, int id,
724
725
            const std::string& name, QueryType qtype) :
        iterator_type_(qtype == QT_NSEC3 ? ITT_NSEC3 : ITT_NAME),
726
        accessor_(accessor),
727
        statement_(NULL),
728
729
730
        statement2_(NULL),
        rc_(SQLITE_OK),
        rc2_(SQLITE_OK),
731
        name_(name)
732
    {
733
734
        // Choose the statement text depending on the query type, and
        // prepare a statement to get data from it.
735
736
        switch (qtype) {
            case QT_ANY:
737
738
739
740
                statement_ = prepare(accessor->dbparameters_->db_,
                                     text_statements[ANY]);
                bindZoneId(id);
                bindName(name_);
741
742
                break;
            case QT_SUBDOMAINS:
743
744
745
746
747
                statement_ = prepare(accessor->dbparameters_->db_,
                                     text_statements[ANY_SUB]);
                bindZoneId(id);
                // Done once, this should not be very inefficient.
                bindName(isc::dns::Name(name_).reverse().toText() + "%");
748
749
                break;
            case QT_NSEC3:
750
751
752
753
                statement_ = prepare(accessor->dbparameters_->db_,
                                     text_statements[NSEC3]);
                bindZoneId(id);
                bindName(name_);
754
755
756
757
758
759
760
761
762
                break;
            default:
                // Can Not Happen - there isn't any other type of query
                // and all the calls to the constructor are from this
                // file. Therefore no way to test it throws :-(.
                isc_throw(Unexpected,
                          "Invalid qtype passed - unreachable code branch "
                          "reached");
        }
763
764
    }

765
    bool getNext(std::string (&data)[COLUMN_COUNT]) {
766
        // If there's another row, get it
767
768
        // If finalize has been called (e.g. when previous getNext() got
        // SQLITE_DONE), directly return false
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
        while (statement_ != NULL) {
            rc_ = sqlite3_step(statement_);
            if (rc_ == SQLITE_ROW) {
                // For both types, we copy the first four columns
                copyColumn(data, TYPE_COLUMN);
                copyColumn(data, TTL_COLUMN);
                // The NSEC3 lookup does not provide the SIGTYPE, it is not
                // necessary and not contained in the table.
                if (iterator_type_ != ITT_NSEC3) {
                    copyColumn(data, SIGTYPE_COLUMN);
                }
                copyColumn(data, RDATA_COLUMN);
                // Only copy Name if we are iterating over every record
                if (iterator_type_ == ITT_ALL) {
                    copyColumn(data, NAME_COLUMN);
                }
                return (true);
            } else if (rc_ != SQLITE_DONE) {
                isc_throw(DataSourceError,
                          "Unexpected failure in sqlite3_step: " <<
                          sqlite3_errmsg(accessor_->dbparameters_->db_));
790
            }
791
792
793
794
            // We are done with statement_. If statement2_ has not been
            // used yet, try that one now.
            if ((statement2_ == NULL) || (rc2_ != SQLITE_OK)) {
                break;
795
            }
796
797
            std::swap(statement_, statement2_);
            std::swap(rc_, rc2_);
798
        }
799
        finalize();
800
801
        return (false);
    }
802

803
    virtual ~Context() {
804
        finalize();
805
806
807
    }

private:
808
809
810
811
812
    // Depending on which constructor is called, behaviour is slightly
    // different. We keep track of what to do with the iterator type
    // See description of getNext() and the constructors
    enum IteratorType {
        ITT_ALL,
813
814
        ITT_NAME,
        ITT_NSEC3
815
816
    };

817
    void copyColumn(std::string (&data)[COLUMN_COUNT], int column) {
Jelte Jansen's avatar
Jelte Jansen committed
818
        data[column] = convertToPlainChar(sqlite3_column_text(statement_,
819
820
                                                              column),
                                          accessor_->dbparameters_->db_);
821
822
    }

Jelte Jansen's avatar
Jelte Jansen committed
823
824
    void bindZoneId(const int zone_id) {
        if (sqlite3_bind_int(statement_, 1, zone_id) != SQLITE_OK) {
825
            finalize();
Jelte Jansen's avatar
Jelte Jansen committed
826
827
            isc_throw(SQLite3Error, "Could not bind int " << zone_id <<
                      " to SQL statement: " <<
828
                      sqlite3_errmsg(accessor_->dbparameters_->db_));
Jelte Jansen's avatar
Jelte Jansen committed
829
830
831
        }
    }

832
833
    void bindName(const std::string& name) {
        if (sqlite3_bind_text(statement_, 2, name.c_str(), -1,
Jelte Jansen's avatar
Jelte Jansen committed
834
                              SQLITE_TRANSIENT) != SQLITE_OK) {
835
            const char* errmsg = sqlite3_errmsg(accessor_->dbparameters_->db_);
836
            finalize();
Jelte Jansen's avatar
Jelte Jansen committed
837
838
839
840
841
            isc_throw(SQLite3Error, "Could not bind text '" << name <<
                      "' to SQL statement: " << errmsg);
        }
    }

842
    void finalize() {
843
844
845
846
847
848
849
850
        if (statement_ != NULL) {
             sqlite3_finalize(statement_);
             statement_ = NULL;
        }
        if (statement2_ != NULL) {
             sqlite3_finalize(statement2_);
             statement2_ = NULL;
        }
851
852
    }

853
    const IteratorType iterator_type_;
854
    boost::shared_ptr<const SQLite3Accessor> accessor_;
855
    sqlite3_stmt* statement_;
856
857
858
    sqlite3_stmt* statement2_;
    int rc_;
    int rc2_;
859
    const std::string name_;
860
861
};

862
863
864

// Methods to retrieve the various iterators

865
DatabaseAccessor::IteratorContextPtr
866
SQLite3Accessor::getRecords(const std::string& name, int id,
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
867
868
869
                            bool subdomains) const
{
    return (IteratorContextPtr(new Context(shared_from_this(), id, name,
870
871
872
                                           subdomains ?
                                           Context::QT_SUBDOMAINS :
                                           Context::QT_ANY)));
873
874
}

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
875
DatabaseAccessor::IteratorContextPtr
876
877
878
SQLite3Accessor::getNSEC3Records(const std::string& hash, int id) const {
    return (IteratorContextPtr(new Context(shared_from_this(), id, hash,
                                           Context::QT_NSEC3)));
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
879
880
}

881
DatabaseAccessor::IteratorContextPtr
882
SQLite3Accessor::getAllRecords(int id) const {
883
    return (IteratorContextPtr(new Context(shared_from_this(), id)));
884
885
}

886
887
888

/// \brief Difference Iterator
///
889
890
/// This iterator is used to search through the differences table for the
/// resouce records making up an IXFR between two versions of a zone.
891

892
// cppcheck-suppress noConstructor
893
894
895
class SQLite3Accessor::DiffContext : public DatabaseAccessor::IteratorContext {
public:

896
897
    /// \brief Constructor
    ///
898
899
900
901
902
    /// Constructs the iterator for the difference sequence.  It is
    /// passed two parameters, the first and last versions in the difference
    /// sequence.  Note that because of serial number rollover, it may well
    /// be that the start serial number is greater than the end one.
    ///
903
    /// \param zone_id ID of the zone (in the zone table)
904
905
906
907
    /// \param start Serial number of first version in difference sequence
    /// \param end Serial number of last version in difference sequence
    ///
    /// \exception any A number of exceptions can be expected
908
    DiffContext(const boost::shared_ptr<const SQLite3Accessor>& accessor,
909
                int zone_id, uint32_t start, uint32_t end) :
910
911
        accessor_(accessor),
        last_status_(SQLITE_ROW)
912
    {
913
        try {
Stephen Morris's avatar
Stephen Morris committed
914
915
            int low_id = findIndex(LOW_DIFF_ID, zone_id, start, DIFF_DELETE);
            int high_id = findIndex(HIGH_DIFF_ID, zone_id, end, DIFF_ADD);
916
917

            // Prepare the statement that will return data values
918
            reset(DIFF_RECS);
919
920
921
            bindInt(DIFF_RECS, 1, zone_id);
            bindInt(DIFF_RECS, 2, low_id);
            bindInt(DIFF_RECS, 3, high_id);
922

923
        } catch (...) {
924
            // Something wrong, clear up everything.
925
926
927
            accessor_->dbparameters_->finalizeStatements();
            throw;
        }
928
929
    }

930
931
932
    /// \brief Destructor
    virtual ~DiffContext()
    {}
933

934
935
936
937
938
939
940
941
942
    /// \brief Get Next Diff Record
    ///
    /// Returns the next difference record in the difference sequence.
    ///
    /// \param data Array of std::strings COLUMN_COUNT long.  The results
    ///        are returned in this.
    ///
    /// \return bool true if data is returned, false if not.
    ///
943
    /// \exception any Varied
944
945
946
    bool getNext(std::string (&data)[COLUMN_COUNT]) {

        if (last_status_ != SQLITE_DONE) {
947
948
949
            // Last call (if any) didn't reach end of result set, so we
            // can read another row from it.
            //
Jelte Jansen's avatar
Jelte Jansen committed
950
951
952
953
954
            // Get a pointer to the statement for brevity (this does not
            // transfer ownership of the statement to this class, so there is
            // no need to tidy up after we have finished using it).
            sqlite3_stmt* stmt =
                accessor_->dbparameters_->getStatement(DIFF_RECS);
955

956
957
958
959
960
961
962
            const int rc(sqlite3_step(stmt));
            if (rc == SQLITE_ROW) {
                // Copy the data across to the output array
                copyColumn(DIFF_RECS, data, TYPE_COLUMN);
                copyColumn(DIFF_RECS, data, TTL_COLUMN);
                copyColumn(DIFF_RECS, data, NAME_COLUMN);
                copyColumn(DIFF_RECS, data, RDATA_COLUMN);
963

964
965
966
967
968
969
970
971
            } else if (rc != SQLITE_DONE) {
                isc_throw(DataSourceError,
                          "Unexpected failure in sqlite3_step: " <<
                          sqlite3_errmsg(accessor_->dbparameters_->db_));
            }
            last_status_ = rc;
        }
        return (last_status_ == SQLITE_ROW);
972
973
    }

974
975
private:

976
    /// \brief Reset prepared statement
977
    ///
978
979
    /// Sets up the statement so that new parameters can be attached to it and
    /// that it can be used to query for another difference sequence.
980
981
    ///
    /// \param stindex Index of prepared statement to which to bind
982
983
    void reset(int stindex) {
        sqlite3_stmt* stmt = accessor_->dbparameters_->getStatement(stindex);
Jelte Jansen's avatar
Jelte Jansen committed
984
985
        if ((sqlite3_reset(stmt) != SQLITE_OK) ||
            (sqlite3_clear_bindings(stmt) != SQLITE_OK)) {
986
            isc_throw(SQLite3Error, "Could not clear statement bindings in '" <<
Jelte Jansen's avatar
Jelte Jansen committed
987
                      text_statements[stindex] << "': " <<
988
989
990
                      sqlite3_errmsg(accessor_->dbparameters_->db_));
        }
    }
Jelte Jansen's avatar
Jelte Jansen committed
991

992
993
994
995
996
997
998
999
    /// \brief Bind Int
    ///
    /// Binds an integer to a specific variable in a prepared statement.
    ///
    /// \param stindex Index of prepared statement to which to bind
    /// \param varindex Index of variable to which to bind
    /// \param value Value of variable to bind
    /// \exception SQLite3Error on an error
1000
1001
    void bindInt(int stindex, int varindex, sqlite3_int64 value) {
        if (sqlite3_bind_int64(accessor_->dbparameters_->getStatement(stindex),
1002
1003
1004
                             varindex, value) != SQLITE_OK) {
            isc_throw(SQLite3Error, "Could not bind value to parameter " <<
                      varindex << " in statement '" <<
Jelte Jansen's avatar
Jelte Jansen committed
1005
                      text_statements[stindex] << "': " <<
1006
1007
1008
1009
                      sqlite3_errmsg(accessor_->dbparameters_->db_));
        }
    }

1010
    ///\brief Get Single Value
1011
    ///
1012
1013
    /// Executes a prepared statement (which has parameters bound to it)
    /// for which the result of a single value is expected.
1014
    ///
1015
    /// \param stindex Index of prepared statement in statement table.
1016
    ///
1017
    /// \return Value of SELECT.
1018
    ///
1019
1020
1021
1022
    /// \exception TooMuchData Multiple rows returned when one expected
    /// \exception TooLittleData Zero rows returned when one expected
    /// \exception DataSourceError SQLite3-related error
    int getSingleValue(StatementID stindex) {
1023

1024
1025
1026
        // Get a pointer to the statement for brevity (does not transfer
        // resources)
        sqlite3_stmt* stmt = accessor_->dbparameters_->getStatement(stindex);
1027
1028
1029
1030
1031
1032
1033

        // Execute the data.  Should be just one result
        int rc = sqlite3_step(stmt);
        int result = -1;
        if (rc == SQLITE_ROW) {

            // Got some data, extract the value
1034
            result = sqlite3_column_int(stmt, 0);
Stephen Morris's avatar
Stephen Morris committed
1035
            rc = sqlite3_step(stmt);
1036
1037
            if (rc == SQLITE_DONE) {

1038
1039
1040
                // All OK, exit with the value.
                return (result);

1041
            } else if (rc == SQLITE_ROW) {
1042
1043
                isc_throw(TooMuchData, "request to return one value from "
                          "diffs table returned multiple values");
1044
1045
1046
            }
        } else if (rc == SQLITE_DONE) {

1047
1048
1049
1050
            // No data in the table.  A bare exception with no explanation is
            // thrown, as it will be replaced by a more informative one by
            // the caller.
            isc_throw(TooLittleData, "");
1051
1052
        }

1053
1054
1055
        // We get here on an error.
        isc_throw(DataSourceError, "could not get data from diffs table: " <<
                  sqlite3_errmsg(accessor_->dbparameters_->db_));
1056

1057
        // Keep the compiler happy with a return value.
1058
1059
1060
        return (result);
    }

1061
1062
1063
1064
1065
1066
1067
1068
    /// \brief Find index
    ///
    /// Executes the prepared statement locating the high or low index in
    /// the diffs table and returns that index.
    ///
    /// \param stmt_id Index of the prepared statement to execute
    /// \param zone_id ID of the zone for which the index is being sought
    /// \param serial Zone serial number for which an index is being sought.
Stephen Morris's avatar
Stephen Morris committed
1069
    /// \param diff Code to delete record additions or deletions
1070
1071
1072
1073
    ///
    /// \return int ID of the row in the difss table corresponding to the
    ///         statement.
    ///
1074
1075
    /// \exception TooLittleData Internal error, no result returned when one
    ///            was expected.
1076
    /// \exception NoSuchSerial Serial number not found.
1077
    /// \exception NoDiffsData No data for this zone found in diffs table
Stephen Morris's avatar
Stephen Morris committed
1078
    int findIndex(StatementID stindex, int zone_id, uint32_t serial, int diff) {
1079
1080

        // Set up the statement
1081
        reset(stindex);
1082
1083
        bindInt(stindex, 1, zone_id);
        bindInt(stindex, 2, serial);
Stephen Morris's avatar
Stephen Morris committed
1084
        bindInt(stindex, 3, diff);
1085
1086
1087
1088
1089
1090

        // Execute the statement
        int result = -1;
        try {
            result = getSingleValue(stindex);

Stephen Morris's avatar
Stephen Morris committed
1091
        } catch (const TooLittleData&) {
1092

1093
1094
1095
            // No data returned but the SQL query succeeded.  Only possibility
            // is that there is no entry in the differences table for the given
            // zone and version.
1096
            isc_throw(NoSuchSerial, "No entry in differences table for" <<
1097
                      " zone ID " << zone_id << ", serial number " << serial);
1098
1099
1100
1101
1102
        }

        return (result);
    }

1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
    /// \brief Copy Column to Output
    ///
    /// Copies the textual data in the result set to the specified column
    /// in the output.
    ///
    /// \param stindex Index of prepared statement used to access data
    /// \param data Array of columns passed to getNext
    /// \param column Column of output to copy
    void copyColumn(StatementID stindex, std::string (&data)[COLUMN_COUNT],
                    int column) {

        // Get a pointer to the statement for brevity (does not transfer
        // resources)
        sqlite3_stmt* stmt = accessor_->dbparameters_->getStatement(stindex);
        data[column] = convertToPlainChar(sqlite3_column_text(stmt,
                                                              column),
                                          accessor_->dbparameters_->db_);
    }
Jelte Jansen's avatar
Jelte Jansen committed
1121

1122
1123
    // Attributes

1124
    boost::shared_ptr<const SQLite3Accessor> accessor_; // Accessor object
1125
    int last_status_;           // Last status received from sqlite3_step
1126
1127
1128
1129
1130
};

// ... and return the iterator

DatabaseAccessor::IteratorContextPtr
1131
SQLite3Accessor::getDiffs(int id, uint32_t start, uint32_t end) const {
1132
1133
1134
1135
1136
1137
    return (IteratorContextPtr(new DiffContext(shared_from_this(), id, start,
                               end)));
}



1138
pair<bool, int>
1139
SQLite3Accessor::startUpdateZone(const string& zone_name, const bool replace) {
1140
    if (dbparameters_->updating_zone) {
1141
1142
1143
        isc_throw(DataSourceError,
                  "duplicate zone update on SQLite3 data source");
    }
1144
1145
1146
1147
    if (dbparameters_->in_transaction) {
        isc_throw(DataSourceError,
                  "zone update attempt in another SQLite3 transaction");
    }
1148
1149
1150
1151
1152
1153

    const pair<bool, int> zone_info(getZone(zone_name));
    if (!zone_info.first) {
        return (zone_info);