Branch data Line data Source code
1 : : // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #include <sqlite3.h>
16 : :
17 : : #include <string>
18 : : #include <utility>
19 : : #include <vector>
20 : :
21 : : #include <exceptions/exceptions.h>
22 : :
23 : : #include <datasrc/sqlite3_accessor.h>
24 : : #include <datasrc/logger.h>
25 : : #include <datasrc/data_source.h>
26 : : #include <datasrc/factory.h>
27 : : #include <datasrc/database.h>
28 : : #include <util/filename.h>
29 : :
30 : : using namespace std;
31 : : using namespace isc::data;
32 : :
33 : : namespace {
34 : : // Expected schema. The major version must match else there is an error. If
35 : : // the minor version of the database is less than this, a warning is output.
36 : : //
37 : : // It is assumed that a program written to run on m.n of the database will run
38 : : // with a database version m.p, where p is any number. However, if p < n,
39 : : // we assume that the database structure was upgraded for some reason, and that
40 : : // some advantage may result if the database is upgraded. Conversely, if p > n,
41 : : // The database is at a later version than the program was written for and the
42 : : // program may not be taking advantage of features (possibly performance
43 : : // improvements) added to the database.
44 : : const int SQLITE_SCHEMA_MAJOR_VERSION = 2;
45 : : const int SQLITE_SCHEMA_MINOR_VERSION = 0;
46 : : }
47 : :
48 : : namespace isc {
49 : : namespace datasrc {
50 : :
51 : : // The following enum and char* array define the SQL statements commonly
52 : : // used in this implementation. Corresponding prepared statements (of
53 : : // type sqlite3_stmt*) are maintained in the statements_ array of the
54 : : // SQLite3Parameters structure.
55 : :
56 : : enum StatementID {
57 : : ZONE = 0,
58 : : ANY = 1,
59 : : ANY_SUB = 2,
60 : : BEGIN = 3,
61 : : COMMIT = 4,
62 : : ROLLBACK = 5,
63 : : DEL_ZONE_RECORDS = 6,
64 : : ADD_RECORD = 7,
65 : : DEL_RECORD = 8,
66 : : ITERATE = 9,
67 : : FIND_PREVIOUS = 10,
68 : : ADD_RECORD_DIFF = 11,
69 : : LOW_DIFF_ID = 12,
70 : : HIGH_DIFF_ID = 13,
71 : : DIFF_RECS = 14,
72 : : NSEC3 = 15,
73 : : NSEC3_PREVIOUS = 16,
74 : : NSEC3_LAST = 17,
75 : : ADD_NSEC3_RECORD = 18,
76 : : DEL_ZONE_NSEC3_RECORDS = 19,
77 : : DEL_NSEC3_RECORD = 20,
78 : : NUM_STATEMENTS = 21
79 : : };
80 : :
81 : : const char* const text_statements[NUM_STATEMENTS] = {
82 : : // note for ANY and ITERATE: the order of the SELECT values is
83 : : // specifically chosen to match the enum values in RecordColumns
84 : : "SELECT id FROM zones WHERE name=?1 AND rdclass = ?2", // ZONE
85 : : "SELECT rdtype, ttl, sigtype, rdata FROM records " // ANY
86 : : "WHERE zone_id=?1 AND name=?2",
87 : : "SELECT rdtype, ttl, sigtype, rdata " // ANY_SUB
88 : : "FROM records WHERE zone_id=?1 AND name LIKE (\"%.\" || ?2)",
89 : : "BEGIN", // BEGIN
90 : : "COMMIT", // COMMIT
91 : : "ROLLBACK", // ROLLBACK
92 : : "DELETE FROM records WHERE zone_id=?1", // DEL_ZONE_RECORDS
93 : : "INSERT INTO records " // ADD_RECORD
94 : : "(zone_id, name, rname, ttl, rdtype, sigtype, rdata) "
95 : : "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
96 : : "DELETE FROM records WHERE zone_id=?1 AND name=?2 " // DEL_RECORD
97 : : "AND rdtype=?3 AND rdata=?4",
98 : : // The following iterates the whole zone. As the NSEC3 records
99 : : // (and corresponding RRSIGs) live in separate table, we need to
100 : : // take both of them. As the RRSIGs are for NSEC3s in the other
101 : : // table, we can easily hardcode the sigtype.
102 : : //
103 : : // The extra column is so we can order it by rname. This is to
104 : : // preserve the previous order, mostly for tests.
105 : : // TODO: Is it possible to get rid of the ordering?
106 : : "SELECT rdtype, ttl, sigtype, rdata, name, rname FROM records " // ITERATE
107 : : "WHERE zone_id = ?1 "
108 : : "UNION "
109 : : "SELECT rdtype, ttl, \"NSEC3\", rdata, owner, owner FROM nsec3 "
110 : : "WHERE zone_id = ?1 ORDER by rname, rdtype",
111 : : /*
112 : : * This one looks for previous name with NSEC record. It is done by
113 : : * using the reversed name. The NSEC is checked because we need to
114 : : * skip glue data, which don't have the NSEC.
115 : : */
116 : : "SELECT name FROM records " // FIND_PREVIOUS
117 : : "WHERE zone_id=?1 AND rdtype = 'NSEC' AND "
118 : : "rname < ?2 ORDER BY rname DESC LIMIT 1",
119 : : "INSERT INTO diffs " // ADD_RECORD_DIFF
120 : : "(zone_id, version, operation, name, rrtype, ttl, rdata) "
121 : : "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
122 : :
123 : : // Two statements to select the lowest ID and highest ID in a set of
124 : : // differences.
125 : : "SELECT id FROM diffs " // LOW_DIFF_ID
126 : : "WHERE zone_id=?1 AND version=?2 and OPERATION=?3 "
127 : : "ORDER BY id ASC LIMIT 1",
128 : : "SELECT id FROM diffs " // HIGH_DIFF_ID
129 : : "WHERE zone_id=?1 AND version=?2 and OPERATION=?3 "
130 : : "ORDER BY id DESC LIMIT 1",
131 : :
132 : : // In the next statement, note the redundant ID. This is to ensure
133 : : // that the columns match the column IDs passed to the iterator
134 : : "SELECT rrtype, ttl, id, rdata, name FROM diffs " // DIFF_RECS
135 : : "WHERE zone_id=?1 AND id>=?2 and id<=?3 "
136 : : "ORDER BY id ASC",
137 : :
138 : : // NSEC3: Query to get the NSEC3 records
139 : : //
140 : : // The "1" in SELECT is for positioning the rdata column to the
141 : : // expected position, so we can reuse the same code as for other
142 : : // lookups.
143 : : "SELECT rdtype, ttl, 1, rdata FROM nsec3 WHERE zone_id=?1 AND "
144 : : "hash=?2",
145 : : // NSEC3_PREVIOUS: For getting the previous NSEC3 hash
146 : : "SELECT DISTINCT hash FROM nsec3 WHERE zone_id=?1 AND hash < ?2 "
147 : : "ORDER BY hash DESC LIMIT 1",
148 : : // NSEC3_LAST: And for wrap-around
149 : : "SELECT DISTINCT hash FROM nsec3 WHERE zone_id=?1 "
150 : : "ORDER BY hash DESC LIMIT 1",
151 : : // ADD_NSEC3_RECORD: Add NSEC3-related (NSEC3 or NSEC3-covering RRSIG) RR
152 : : "INSERT INTO nsec3 (zone_id, hash, owner, ttl, rdtype, rdata) "
153 : : "VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
154 : : // DEL_ZONE_NSEC3_RECORDS: delete all NSEC3-related records from the zone
155 : : "DELETE FROM nsec3 WHERE zone_id=?1",
156 : : // DEL_NSEC3_RECORD: delete specified NSEC3-related records
157 : : "DELETE FROM nsec3 WHERE zone_id=?1 AND hash=?2 "
158 : : "AND rdtype=?3 AND rdata=?4"
159 : : };
160 : :
161 : 1337 : struct SQLite3Parameters {
162 : : SQLite3Parameters() :
163 : : db_(NULL), major_version_(-1), minor_version_(-1),
164 : 1337 : in_transaction(false), updating_zone(false), updated_zone_id(-1)
165 : : {
166 [ + + ][ + + ]: 29414 : for (int i = 0; i < NUM_STATEMENTS; ++i) {
[ + + ]
167 : 28077 : statements_[i] = NULL;
168 : : }
169 : : }
170 : :
171 : : // This method returns the specified ID of SQLITE3 statement. If it's
172 : : // not yet prepared it internally creates a new one. This way we can
173 : : // avoid preparing unnecessary statements and minimize the overhead.
174 : : sqlite3_stmt*
175 : 13289 : getStatement(int id) {
176 [ - + ]: 13289 : assert(id < NUM_STATEMENTS);
177 [ + + ]: 13289 : if (statements_[id] == NULL) {
178 [ - + ]: 1240 : assert(db_ != NULL);
179 : 1240 : sqlite3_stmt* prepared = NULL;
180 [ - + ]: 1240 : if (sqlite3_prepare_v2(db_, text_statements[id], -1, &prepared,
181 : 1240 : NULL) != SQLITE_OK) {
182 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Could not prepare SQLite statement: "
[ # # ][ # # ]
[ # # ][ # # ]
183 : : << text_statements[id] <<
184 : : ": " << sqlite3_errmsg(db_));
185 : : }
186 : 1240 : statements_[id] = prepared;
187 : : }
188 : 13289 : return (statements_[id]);
189 : : }
190 : :
191 : : void
192 : 449 : finalizeStatements() {
193 [ + + ]: 9878 : for (int i = 0; i < NUM_STATEMENTS; ++i) {
194 [ + + ]: 9429 : if (statements_[i] != NULL) {
195 : 1240 : sqlite3_finalize(statements_[i]);
196 : 1240 : statements_[i] = NULL;
197 : : }
198 : : }
199 : 449 : }
200 : :
201 : : sqlite3* db_;
202 : : int major_version_;
203 : : int minor_version_;
204 : : bool in_transaction; // whether or not a transaction has been started
205 : : bool updating_zone; // whether or not updating the zone
206 : : int updated_zone_id; // valid only when in_transaction is true
207 : : string updated_zone_origin_; // ditto, and only needed to handle NSEC3s
208 : : private:
209 : : // statements_ are private and must be accessed via getStatement() outside
210 : : // of this structure.
211 : : sqlite3_stmt* statements_[NUM_STATEMENTS];
212 : : };
213 : :
214 : : // This is a helper class to encapsulate the code logic of executing
215 : : // a specific SQLite3 statement, ensuring the corresponding prepared
216 : : // statement is always reset whether the execution is completed successfully
217 : : // or it results in an exception.
218 : : // Note that an object of this class is intended to be used for "ephemeral"
219 : : // statement, which is completed with a single "step" (normally within a
220 : : // single call to an SQLite3Database method). In particular, it cannot be
221 : : // used for "SELECT" variants, which generally expect multiple matching rows.
222 : : //
223 : : // The bindXXX methods are straightforward wrappers for the corresponding
224 : : // sqlite3_bind_xxx functions that make bindings with the given parameters
225 : : // on the statement maintained in this class.
226 : : class StatementProcessor {
227 : : public:
228 : : // desc will be used on failure in the what() message of the resulting
229 : : // DataSourceError exception.
230 : 8917 : StatementProcessor(SQLite3Parameters& dbparameters, StatementID stmt_id,
231 : : const char* desc) :
232 : 8917 : dbparameters_(dbparameters), stmt_(dbparameters.getStatement(stmt_id)),
233 : 17834 : desc_(desc)
234 : : {
235 : 8917 : sqlite3_clear_bindings(stmt_);
236 : 8917 : }
237 : :
238 : 8917 : ~StatementProcessor() {
239 : 8917 : sqlite3_reset(stmt_);
240 : 8917 : }
241 : :
242 : 9319 : void bindInt(int index, int val) {
243 [ - + ]: 9319 : if (sqlite3_bind_int(stmt_, index, val) != SQLITE_OK) {
244 [ # # ][ # # ]: 0 : isc_throw(DataSourceError,
[ # # ][ # # ]
245 : : "failed to bind SQLite3 parameter: " <<
246 : : sqlite3_errmsg(dbparameters_.db_));
247 : : }
248 : 9319 : }
249 : :
250 : 836 : void bindInt64(int index, sqlite3_int64 val) {
251 [ - + ]: 836 : if (sqlite3_bind_int64(stmt_, index, val) != SQLITE_OK) {
252 [ # # ][ # # ]: 0 : isc_throw(DataSourceError,
[ # # ][ # # ]
253 : : "failed to bind SQLite3 parameter: " <<
254 : : sqlite3_errmsg(dbparameters_.db_));
255 : : }
256 : 836 : }
257 : :
258 : : // For simplicity, we assume val is a NULL-terminated string, and the
259 : : // entire non NUL characters are to be bound. The destructor parameter
260 : : // is normally either SQLITE_TRANSIENT or SQLITE_STATIC.
261 : 46509 : void bindText(int index, const char* val, void(*destructor)(void*)) {
262 [ - + ]: 46509 : if (sqlite3_bind_text(stmt_, index, val, -1, destructor)
263 : : != SQLITE_OK) {
264 [ # # ][ # # ]: 0 : isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
[ # # ][ # # ]
265 : : sqlite3_errmsg(dbparameters_.db_));
266 : : }
267 : 46509 : }
268 : :
269 : 8917 : void exec() {
270 [ + + ]: 8917 : if (sqlite3_step(stmt_) != SQLITE_DONE) {
271 : 5 : sqlite3_reset(stmt_);
272 [ + - ][ + - ]: 10 : isc_throw(DataSourceError, "failed to " << desc_ << ": " <<
[ + - ][ + - ]
[ + - ][ + - ]
273 : : sqlite3_errmsg(dbparameters_.db_));
274 : : }
275 : 8912 : }
276 : :
277 : : private:
278 : : SQLite3Parameters& dbparameters_;
279 : : sqlite3_stmt* stmt_;
280 : : const char* const desc_;
281 : : };
282 : :
283 : 448 : SQLite3Accessor::SQLite3Accessor(const std::string& filename,
284 : : const string& rrclass) :
285 : : dbparameters_(new SQLite3Parameters),
286 : : filename_(filename),
287 : : class_(rrclass),
288 : : database_name_("sqlite3_" +
289 [ + - ][ + - ]: 1351 : isc::util::Filename(filename).nameAndExtension())
[ + - ]
290 : : {
291 [ + - ][ + - ]: 448 : LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_NEWCONN);
[ + - ][ + - ]
292 : :
293 [ + + ]: 448 : open(filename);
294 : 441 : }
295 : :
296 : : boost::shared_ptr<DatabaseAccessor>
297 : 138 : SQLite3Accessor::clone() {
298 : : return (boost::shared_ptr<DatabaseAccessor>(new SQLite3Accessor(filename_,
299 [ + - ]: 138 : class_)));
300 : : }
301 : :
302 : : namespace {
303 : :
304 : : // This is a helper class to initialize a Sqlite3 DB safely. An object of
305 : : // this class encapsulates all temporary resources that are necessary for
306 : : // the initialization, and release them in the destructor. Once everything
307 : : // is properly initialized, the move() method moves the allocated resources
308 : : // to the main object in an exception free manner. This way, the main code
309 : : // for the initialization can be exception safe, and can provide the strong
310 : : // exception guarantee.
311 : 448 : class Initializer {
312 : : public:
313 : 896 : ~Initializer() {
314 [ + + ]: 448 : if (params_.db_ != NULL) {
315 [ + - ]: 7 : sqlite3_close(params_.db_);
316 : : }
317 : 448 : }
318 : : void move(SQLite3Parameters* dst) {
319 : 441 : *dst = params_;
320 : 441 : params_ = SQLite3Parameters(); // clear everything
321 : : }
322 : : SQLite3Parameters params_;
323 : : };
324 : :
325 : : const char* const SCHEMA_LIST[] = {
326 : : "CREATE TABLE schema_version (version INTEGER NOT NULL, "
327 : : "minor INTEGER NOT NULL DEFAULT 0)",
328 : : "INSERT INTO schema_version VALUES (2, 0)",
329 : : "CREATE TABLE zones (id INTEGER PRIMARY KEY, "
330 : : "name TEXT NOT NULL COLLATE NOCASE, "
331 : : "rdclass TEXT NOT NULL COLLATE NOCASE DEFAULT 'IN', "
332 : : "dnssec BOOLEAN NOT NULL DEFAULT 0)",
333 : : "CREATE INDEX zones_byname ON zones (name)",
334 : : "CREATE TABLE records (id INTEGER PRIMARY KEY, "
335 : : "zone_id INTEGER NOT NULL, name TEXT NOT NULL COLLATE NOCASE, "
336 : : "rname TEXT NOT NULL COLLATE NOCASE, ttl INTEGER NOT NULL, "
337 : : "rdtype TEXT NOT NULL COLLATE NOCASE, sigtype TEXT COLLATE NOCASE, "
338 : : "rdata TEXT NOT NULL)",
339 : : "CREATE INDEX records_byname ON records (name)",
340 : : "CREATE INDEX records_byrname ON records (rname)",
341 : : // The next index is a tricky one. It's necessary for
342 : : // FIND_PREVIOUS to use the index efficiently; since there's an
343 : : // "inequality", the rname column must be placed later. records_byrname
344 : : // may not be sufficient especially when the zone is not signed (and
345 : : // defining a separate index for rdtype only doesn't work either; SQLite3
346 : : // would then create a temporary B-tree for "ORDER BY").
347 : : "CREATE INDEX records_bytype_and_rname ON records (rdtype, rname)",
348 : : "CREATE TABLE nsec3 (id INTEGER PRIMARY KEY, zone_id INTEGER NOT NULL, "
349 : : "hash TEXT NOT NULL COLLATE NOCASE, "
350 : : "owner TEXT NOT NULL COLLATE NOCASE, "
351 : : "ttl INTEGER NOT NULL, rdtype TEXT NOT NULL COLLATE NOCASE, "
352 : : "rdata TEXT NOT NULL)",
353 : : "CREATE INDEX nsec3_byhash ON nsec3 (hash)",
354 : : "CREATE TABLE diffs (id INTEGER PRIMARY KEY, "
355 : : "zone_id INTEGER NOT NULL, "
356 : : "version INTEGER NOT NULL, "
357 : : "operation INTEGER NOT NULL, "
358 : : "name TEXT NOT NULL COLLATE NOCASE, "
359 : : "rrtype TEXT NOT NULL COLLATE NOCASE, "
360 : : "ttl INTEGER NOT NULL, "
361 : : "rdata TEXT NOT NULL)",
362 : : NULL
363 : : };
364 : :
365 : : sqlite3_stmt*
366 : 1870 : prepare(sqlite3* const db, const char* const statement) {
367 : 1870 : sqlite3_stmt* prepared = NULL;
368 [ - + ]: 1870 : if (sqlite3_prepare_v2(db, statement, -1, &prepared, NULL) != SQLITE_OK) {
369 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Could not prepare SQLite statement: " <<
[ # # ][ # # ]
[ # # ][ # # ]
370 : : statement << ": " << sqlite3_errmsg(db));
371 : : }
372 : 1870 : return (prepared);
373 : : }
374 : :
375 : : // small function to sleep for 0.1 seconds, needed when waiting for
376 : : // exclusive database locks (which should only occur on startup, and only
377 : : // when the database has not been created yet)
378 : 50 : void doSleep() {
379 : : struct timespec req;
380 : 50 : req.tv_sec = 0;
381 : 50 : req.tv_nsec = 100000000;
382 : 50 : nanosleep(&req, NULL);
383 : 50 : }
384 : :
385 : : // returns the schema version if the schema version table exists
386 : : // returns -1 if it does not
387 : 897 : int checkSchemaVersionElement(sqlite3* db, const char* const query) {
388 : 897 : sqlite3_stmt* prepared = NULL;
389 : : // At this point in time, the database might be exclusively locked, in
390 : : // which case even prepare() will return BUSY, so we may need to try a
391 : : // few times
392 [ + + ]: 947 : for (size_t i = 0; i < 50; ++i) {
393 : 946 : int rc = sqlite3_prepare_v2(db, query, -1, &prepared, NULL);
394 [ + + ]: 946 : if (rc == SQLITE_ERROR) {
395 : : // this is the error that is returned when the table does not
396 : : // exist
397 : : return (-1);
398 [ + + ]: 936 : } else if (rc == SQLITE_OK) {
399 : : break;
400 [ - + ]: 50 : } else if (rc != SQLITE_BUSY || i == 50) {
401 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Unable to prepare version query: "
[ # # ][ # # ]
[ # # ][ # # ]
402 : : << rc << " " << sqlite3_errmsg(db));
403 : : }
404 : 50 : doSleep();
405 : : }
406 [ + + ]: 887 : if (sqlite3_step(prepared) != SQLITE_ROW) {
407 [ + - ][ + - ]: 4 : isc_throw(SQLite3Error,
[ + - ][ + - ]
408 : : "Unable to query version: " << sqlite3_errmsg(db));
409 : : }
410 : 885 : int version = sqlite3_column_int(prepared, 0);
411 : 885 : sqlite3_finalize(prepared);
412 : 895 : return (version);
413 : : }
414 : :
415 : : // Returns the schema major and minor version numbers in a pair.
416 : : // Returns (-1, -1) if the table does not exist, (1, 0) for a V1
417 : : // database, and (n, m) for any other.
418 : 455 : pair<int, int> checkSchemaVersion(sqlite3* db) {
419 : : int major = checkSchemaVersionElement(db,
420 : 455 : "SELECT version FROM schema_version");
421 [ + + ]: 453 : if (major == -1) {
422 : : return (make_pair(-1, -1));
423 [ + + ]: 443 : } else if (major == 1) {
424 : : return (make_pair(1, 0));
425 : : } else {
426 : : int minor = checkSchemaVersionElement(db,
427 : 442 : "SELECT minor FROM schema_version");
428 : : return (make_pair(major, minor));
429 : : }
430 : : }
431 : :
432 : : // A helper class used in createDatabase() below so we manage the one shot
433 : : // transaction safely.
434 : : class ScopedTransaction {
435 : : public:
436 : 5 : ScopedTransaction(sqlite3* db) : db_(NULL) {
437 : : // try for 5 secs (50*0.1)
438 [ + - ]: 5 : for (size_t i = 0; i < 50; ++i) {
439 : : const int rc = sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION",
440 : 5 : NULL, NULL, NULL);
441 [ - + ]: 5 : if (rc == SQLITE_OK) {
442 : : break;
443 [ # # ]: 0 : } else if (rc != SQLITE_BUSY || i == 50) {
444 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Unable to acquire exclusive lock "
[ # # ][ # # ]
445 : : "for database creation: " << sqlite3_errmsg(db));
446 : : }
447 : 0 : doSleep();
448 : : }
449 : : // Hold the DB pointer once we have successfully acquired the lock.
450 : 5 : db_ = db;
451 : : }
452 : 5 : ~ScopedTransaction() {
453 [ - + ]: 5 : if (db_ != NULL) {
454 : : // Note: even rollback could fail in theory, but in that case
455 : : // we cannot do much for safe recovery anyway. We could at least
456 : : // log the event, but for now don't even bother to do that, with
457 : : // the expectation that we'll soon stop creating the schema in this
458 : : // module.
459 : 0 : sqlite3_exec(db_, "ROLLBACK", NULL, NULL, NULL);
460 : : }
461 : 5 : }
462 : : void commit() {
463 [ + - ][ - + ]: 5 : if (sqlite3_exec(db_, "COMMIT TRANSACTION", NULL, NULL, NULL) !=
464 : : SQLITE_OK) {
465 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Unable to commit newly created database "
[ # # ][ # # ]
[ # # ]
466 : : "schema: " << sqlite3_errmsg(db_));
467 : : }
468 : 5 : db_ = NULL;
469 : : }
470 : :
471 : : private:
472 : : sqlite3* db_;
473 : : };
474 : :
475 : : // return db version
476 : : pair<int, int>
477 : 5 : createDatabase(sqlite3* db) {
478 : 5 : logger.info(DATASRC_SQLITE_SETUP);
479 : :
480 : : // try to get an exclusive lock. Once that is obtained, do the version
481 : : // check *again*, just in case this process was racing another
482 : 5 : ScopedTransaction trasaction(db);
483 [ + - ]: 5 : pair<int, int> schema_version = checkSchemaVersion(db);
484 [ + - ]: 5 : if (schema_version.first == -1) {
485 [ + + ]: 60 : for (int i = 0; SCHEMA_LIST[i] != NULL; ++i) {
486 [ + - ][ - + ]: 55 : if (sqlite3_exec(db, SCHEMA_LIST[i], NULL, NULL, NULL) !=
487 : : SQLITE_OK) {
488 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error,
[ # # ][ # # ]
489 : : "Failed to set up schema " << SCHEMA_LIST[i]);
490 : : }
491 : : }
492 : : trasaction.commit();
493 : :
494 : : // Return the version. We query again to ensure that the only point
495 : : // in which the current schema version is defined is in the create
496 : : // statements.
497 [ + - ]: 5 : schema_version = checkSchemaVersion(db);
498 : : }
499 : :
500 : 5 : return (schema_version);
501 : : }
502 : :
503 : : void
504 : 445 : checkAndSetupSchema(Initializer* initializer) {
505 : 445 : sqlite3* const db = initializer->params_.db_;
506 : :
507 : 445 : pair<int, int> schema_version = checkSchemaVersion(db);
508 [ + + ]: 443 : if (schema_version.first == -1) {
509 : 5 : schema_version = createDatabase(db);
510 [ + + ]: 438 : } else if (schema_version.first != SQLITE_SCHEMA_MAJOR_VERSION) {
511 [ + - ]: 4 : LOG_ERROR(logger, DATASRC_SQLITE_INCOMPATIBLE_VERSION)
512 [ + - ][ + - ]: 2 : .arg(schema_version.first).arg(schema_version.second)
513 [ + - ][ + - ]: 2 : .arg(SQLITE_SCHEMA_MAJOR_VERSION).arg(SQLITE_SCHEMA_MINOR_VERSION);
514 [ + - ][ + - ]: 4 : isc_throw(IncompatibleDbVersion,
[ + - ][ + - ]
515 : : "incompatible SQLite3 database version: " <<
516 : : schema_version.first << "." << schema_version.second);
517 [ - + ]: 436 : } else if (schema_version.second < SQLITE_SCHEMA_MINOR_VERSION) {
518 [ # # ]: 0 : LOG_WARN(logger, DATASRC_SQLITE_COMPATIBLE_VERSION)
519 [ # # ][ # # ]: 0 : .arg(schema_version.first).arg(schema_version.second)
520 [ # # ][ # # ]: 0 : .arg(SQLITE_SCHEMA_MAJOR_VERSION).arg(SQLITE_SCHEMA_MINOR_VERSION);
521 : : }
522 : :
523 : 441 : initializer->params_.major_version_ = schema_version.first;
524 : 441 : initializer->params_.minor_version_ = schema_version.second;
525 : 441 : }
526 : :
527 : : }
528 : :
529 : : void
530 : 448 : SQLite3Accessor::open(const std::string& name) {
531 [ + - ]: 896 : LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CONNOPEN).arg(name);
532 [ - + ]: 448 : if (dbparameters_->db_ != NULL) {
533 : : // There shouldn't be a way to trigger this anyway
534 [ # # ][ # # ]: 0 : isc_throw(DataSourceError, "Duplicate SQLite open with " << name);
[ # # ]
535 : : }
536 : :
537 : 448 : Initializer initializer;
538 : :
539 [ + - ][ + + ]: 448 : if (sqlite3_open(name.c_str(), &initializer.params_.db_) != 0) {
540 [ + - ][ + - ]: 6 : isc_throw(SQLite3Error, "Cannot open SQLite database file: " << name);
[ + - ][ + - ]
541 : : }
542 : :
543 [ + + ]: 445 : checkAndSetupSchema(&initializer);
544 : 882 : initializer.move(dbparameters_.get());
545 : 441 : }
546 : :
547 : 441 : SQLite3Accessor::~SQLite3Accessor() {
548 [ + - ][ + - ]: 441 : LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_DROPCONN);
[ + - ][ + - ]
549 [ + - ]: 441 : if (dbparameters_->db_ != NULL) {
550 [ + - ]: 441 : close();
551 : : }
552 : 810 : }
553 : :
554 : : void
555 : 441 : SQLite3Accessor::close(void) {
556 [ + - ]: 441 : LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CONNCLOSE);
557 [ - + ]: 441 : if (dbparameters_->db_ == NULL) {
558 [ # # ][ # # ]: 0 : isc_throw(DataSourceError,
559 : : "SQLite data source is being closed before open");
560 : : }
561 : :
562 : 441 : dbparameters_->finalizeStatements();
563 : 441 : sqlite3_close(dbparameters_->db_);
564 : 441 : dbparameters_->db_ = NULL;
565 : 441 : }
566 : :
567 : : std::pair<bool, int>
568 : 797 : SQLite3Accessor::getZone(const std::string& name) const {
569 : : int rc;
570 : 797 : sqlite3_stmt* const stmt = dbparameters_->getStatement(ZONE);
571 : :
572 : : // Take the statement (simple SELECT id FROM zones WHERE...)
573 : : // and prepare it (bind the parameters to it)
574 : 797 : sqlite3_reset(stmt);
575 : 797 : rc = sqlite3_bind_text(stmt, 1, name.c_str(), -1, SQLITE_STATIC);
576 [ - + ]: 797 : if (rc != SQLITE_OK) {
577 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Could not bind " << name <<
[ # # ][ # # ]
578 : : " to SQL statement (zone)");
579 : : }
580 : 797 : rc = sqlite3_bind_text(stmt, 2, class_.c_str(), -1, SQLITE_STATIC);
581 [ - + ]: 797 : if (rc != SQLITE_OK) {
582 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Could not bind " << class_ <<
[ # # ][ # # ]
583 : : " to SQL statement (zone)");
584 : : }
585 : :
586 : : // Get the data there and see if it found anything
587 : 797 : rc = sqlite3_step(stmt);
588 [ + + ]: 797 : if (rc == SQLITE_ROW) {
589 : 513 : const int zone_id = sqlite3_column_int(stmt, 0);
590 : 513 : sqlite3_reset(stmt);
591 : 513 : return (pair<bool, int>(true, zone_id));
592 [ + - ]: 284 : } else if (rc == SQLITE_DONE) {
593 : : // Free resources
594 : 284 : sqlite3_reset(stmt);
595 : 284 : return (pair<bool, int>(false, 0));
596 : : }
597 : :
598 : 0 : sqlite3_reset(stmt);
599 [ # # ][ # # ]: 797 : isc_throw(DataSourceError, "Unexpected failure in sqlite3_step: " <<
[ # # ][ # # ]
600 : : sqlite3_errmsg(dbparameters_->db_));
601 : : // Compilers might not realize isc_throw always throws
602 : : return (std::pair<bool, int>(false, 0));
603 : : }
604 : :
605 : : namespace {
606 : :
607 : : // Conversion to plain char
608 : : const char*
609 : 24057 : convertToPlainChar(const unsigned char* ucp, sqlite3 *db) {
610 [ + + ]: 24057 : if (ucp == NULL) {
611 : : // The field can really be NULL, in which case we return an
612 : : // empty string, or sqlite may have run out of memory, in
613 : : // which case we raise an error
614 [ - + ]: 4849 : if (sqlite3_errcode(db) == SQLITE_NOMEM) {
615 [ # # ][ # # ]: 24057 : isc_throw(DataSourceError,
616 : : "Sqlite3 backend encountered a memory allocation "
617 : : "error in sqlite3_column_text()");
618 : : } else {
619 : : return ("");
620 : : }
621 : : }
622 : : const void* p = ucp;
623 : : return (static_cast<const char*>(p));
624 : : }
625 : :
626 : : }
627 : : class SQLite3Accessor::Context : public DatabaseAccessor::IteratorContext {
628 : : public:
629 : : // Construct an iterator for all records. When constructed this
630 : : // way, the getNext() call will copy all fields
631 : : Context(const boost::shared_ptr<const SQLite3Accessor>& accessor, int id) :
632 : : iterator_type_(ITT_ALL),
633 : : accessor_(accessor),
634 : : statement_(NULL),
635 [ + - ]: 24 : name_("")
636 : : {
637 : : // We create the statement now and then just keep getting data from it
638 : 24 : statement_ = prepare(accessor->dbparameters_->db_,
639 [ + - ]: 24 : text_statements[ITERATE]);
640 [ - + ]: 24 : bindZoneId(id);
641 : : }
642 : :
643 : : // What kind of query it is - selection of the statement for DB
644 : : enum QueryType {
645 : : QT_ANY, // Directly for a domain
646 : : QT_SUBDOMAINS, // Subdomains of a given domain
647 : : QT_NSEC3 // Domain in the NSEC3 namespace (the name is is the hash,
648 : : // not the whole name)
649 : : };
650 : :
651 : : // Construct an iterator for records with a specific name. When constructed
652 : : // this way, the getNext() call will copy all fields except name
653 : 1846 : Context(const boost::shared_ptr<const SQLite3Accessor>& accessor, int id,
654 : : const std::string& name, QueryType qtype) :
655 : : iterator_type_(qtype == QT_NSEC3 ? ITT_NSEC3 : ITT_NAME),
656 : : accessor_(accessor),
657 : : statement_(NULL),
658 [ + + ][ + - ]: 3692 : name_(name)
659 : : {
660 : : // Choose the statement text depending on the query type
661 : 1846 : const char* statement(NULL);
662 [ + + - + ]: 1846 : switch (qtype) {
663 : : case QT_ANY:
664 : : statement = text_statements[ANY];
665 : : break;
666 : : case QT_SUBDOMAINS:
667 : 309 : statement = text_statements[ANY_SUB];
668 : 309 : break;
669 : : case QT_NSEC3:
670 : 19 : statement = text_statements[NSEC3];
671 : 19 : break;
672 : : default:
673 : : // Can Not Happen - there isn't any other type of query
674 : : // and all the calls to the constructor are from this
675 : : // file. Therefore no way to test it throws :-(.
676 [ # # ][ # # ]: 0 : isc_throw(Unexpected,
677 : : "Invalid qtype passed - unreachable code branch "
678 : : "reached");
679 : : }
680 : :
681 : : // We create the statement now and then just keep getting data from it
682 [ + - ]: 1846 : statement_ = prepare(accessor->dbparameters_->db_, statement);
683 [ + - ]: 1846 : bindZoneId(id);
684 [ + - ]: 1846 : bindName(name_);
685 : 1846 : }
686 : :
687 : 7084 : bool getNext(std::string (&data)[COLUMN_COUNT]) {
688 : : // If there's another row, get it
689 : : // If finalize has been called (e.g. when previous getNext() got
690 : : // SQLITE_DONE), directly return false
691 [ + + ]: 7084 : if (statement_ == NULL) {
692 : : return false;
693 : : }
694 : 7080 : const int rc(sqlite3_step(statement_));
695 [ + + ]: 7080 : if (rc == SQLITE_ROW) {
696 : : // For both types, we copy the first four columns
697 : 5262 : copyColumn(data, TYPE_COLUMN);
698 : 5262 : copyColumn(data, TTL_COLUMN);
699 : : // The NSEC3 lookup does not provide the SIGTYPE, it is not
700 : : // necessary and not contained in the table.
701 [ + + ]: 5262 : if (iterator_type_ != ITT_NSEC3) {
702 : 5249 : copyColumn(data, SIGTYPE_COLUMN);
703 : : }
704 : 5262 : copyColumn(data, RDATA_COLUMN);
705 : : // Only copy Name if we are iterating over every record
706 [ + + ]: 5262 : if (iterator_type_ == ITT_ALL) {
707 : 414 : copyColumn(data, NAME_COLUMN);
708 : : }
709 : : return (true);
710 [ - + ]: 1818 : } else if (rc != SQLITE_DONE) {
711 [ # # ][ # # ]: 0 : isc_throw(DataSourceError,
[ # # ][ # # ]
712 : : "Unexpected failure in sqlite3_step: " <<
713 : : sqlite3_errmsg(accessor_->dbparameters_->db_));
714 : : }
715 : : finalize();
716 : 7084 : return (false);
717 : : }
718 : :
719 : 1870 : virtual ~Context() {
720 : : finalize();
721 : 3740 : }
722 : :
723 : : private:
724 : : // Depending on which constructor is called, behaviour is slightly
725 : : // different. We keep track of what to do with the iterator type
726 : : // See description of getNext() and the constructors
727 : : enum IteratorType {
728 : : ITT_ALL,
729 : : ITT_NAME,
730 : : ITT_NSEC3
731 : : };
732 : :
733 : 21449 : void copyColumn(std::string (&data)[COLUMN_COUNT], int column) {
734 : : data[column] = convertToPlainChar(sqlite3_column_text(statement_,
735 : : column),
736 : 21449 : accessor_->dbparameters_->db_);
737 : 21449 : }
738 : :
739 : 1870 : void bindZoneId(const int zone_id) {
740 [ - + ]: 1870 : if (sqlite3_bind_int(statement_, 1, zone_id) != SQLITE_OK) {
741 : : finalize();
742 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Could not bind int " << zone_id <<
[ # # ][ # # ]
[ # # ][ # # ]
743 : : " to SQL statement: " <<
744 : : sqlite3_errmsg(accessor_->dbparameters_->db_));
745 : : }
746 : 1870 : }
747 : :
748 : 1846 : void bindName(const std::string& name) {
749 [ - + ]: 1846 : if (sqlite3_bind_text(statement_, 2, name.c_str(), -1,
750 : 1846 : SQLITE_TRANSIENT) != SQLITE_OK) {
751 : 0 : const char* errmsg = sqlite3_errmsg(accessor_->dbparameters_->db_);
752 : : finalize();
753 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Could not bind text '" << name <<
[ # # ][ # # ]
[ # # ]
754 : : "' to SQL statement: " << errmsg);
755 : : }
756 : 1846 : }
757 : :
758 : 0 : void finalize() {
759 [ + - ]: 3688 : sqlite3_finalize(statement_);
760 : 3688 : statement_ = NULL;
761 : 0 : }
762 : :
763 : : const IteratorType iterator_type_;
764 : : boost::shared_ptr<const SQLite3Accessor> accessor_;
765 : : sqlite3_stmt* statement_;
766 : : const std::string name_;
767 : : };
768 : :
769 : :
770 : : // Methods to retrieve the various iterators
771 : :
772 : : DatabaseAccessor::IteratorContextPtr
773 : 1827 : SQLite3Accessor::getRecords(const std::string& name, int id,
774 : : bool subdomains) const
775 : : {
776 : : return (IteratorContextPtr(new Context(shared_from_this(), id, name,
777 : : subdomains ?
778 : : Context::QT_SUBDOMAINS :
779 [ + - ][ + + ]: 3654 : Context::QT_ANY)));
[ + - ][ + - ]
780 : : }
781 : :
782 : : DatabaseAccessor::IteratorContextPtr
783 : 19 : SQLite3Accessor::getNSEC3Records(const std::string& hash, int id) const {
784 : : return (IteratorContextPtr(new Context(shared_from_this(), id, hash,
785 [ + - ][ + - ]: 38 : Context::QT_NSEC3)));
[ + - ]
786 : : }
787 : :
788 : : DatabaseAccessor::IteratorContextPtr
789 : 24 : SQLite3Accessor::getAllRecords(int id) const {
790 [ + - ][ + - ]: 72 : return (IteratorContextPtr(new Context(shared_from_this(), id)));
791 : : }
792 : :
793 : :
794 : : /// \brief Difference Iterator
795 : : ///
796 : : /// This iterator is used to search through the differences table for the
797 : : /// resouce records making up an IXFR between two versions of a zone.
798 : :
799 : : class SQLite3Accessor::DiffContext : public DatabaseAccessor::IteratorContext {
800 : : public:
801 : :
802 : : /// \brief Constructor
803 : : ///
804 : : /// Constructs the iterator for the difference sequence. It is
805 : : /// passed two parameters, the first and last versions in the difference
806 : : /// sequence. Note that because of serial number rollover, it may well
807 : : /// be that the start serial number is greater than the end one.
808 : : ///
809 : : /// \param zone_id ID of the zone (in the zone table)
810 : : /// \param start Serial number of first version in difference sequence
811 : : /// \param end Serial number of last version in difference sequence
812 : : ///
813 : : /// \exception any A number of exceptions can be expected
814 : 26 : DiffContext(const boost::shared_ptr<const SQLite3Accessor>& accessor,
815 : : int zone_id, uint32_t start, uint32_t end) :
816 : : accessor_(accessor),
817 : 86 : last_status_(SQLITE_ROW)
818 : : {
819 : : try {
820 [ + + ]: 26 : int low_id = findIndex(LOW_DIFF_ID, zone_id, start, DIFF_DELETE);
821 [ + + ]: 19 : int high_id = findIndex(HIGH_DIFF_ID, zone_id, end, DIFF_ADD);
822 : :
823 : : // Prepare the statement that will return data values
824 [ + - ]: 18 : reset(DIFF_RECS);
825 [ + - ]: 18 : bindInt(DIFF_RECS, 1, zone_id);
826 [ + - ]: 18 : bindInt(DIFF_RECS, 2, low_id);
827 [ + - ]: 18 : bindInt(DIFF_RECS, 3, high_id);
828 : :
829 : 16 : } catch (...) {
830 : : // Something wrong, clear up everything.
831 [ - + ]: 8 : accessor_->dbparameters_->finalizeStatements();
832 : 8 : throw;
833 : : }
834 : 18 : }
835 : :
836 : : /// \brief Destructor
837 : 36 : virtual ~DiffContext()
838 : 36 : {}
839 : :
840 : : /// \brief Get Next Diff Record
841 : : ///
842 : : /// Returns the next difference record in the difference sequence.
843 : : ///
844 : : /// \param data Array of std::strings COLUMN_COUNT long. The results
845 : : /// are returned in this.
846 : : ///
847 : : /// \return bool true if data is returned, false if not.
848 : : ///
849 : : /// \exception any Varied
850 : 665 : bool getNext(std::string (&data)[COLUMN_COUNT]) {
851 : :
852 [ + + ]: 665 : if (last_status_ != SQLITE_DONE) {
853 : : // Last call (if any) didn't reach end of result set, so we
854 : : // can read another row from it.
855 : : //
856 : : // Get a pointer to the statement for brevity (this does not
857 : : // transfer ownership of the statement to this class, so there is
858 : : // no need to tidy up after we have finished using it).
859 : : sqlite3_stmt* stmt =
860 : 661 : accessor_->dbparameters_->getStatement(DIFF_RECS);
861 : :
862 : 661 : const int rc(sqlite3_step(stmt));
863 [ + + ]: 661 : if (rc == SQLITE_ROW) {
864 : : // Copy the data across to the output array
865 : 644 : copyColumn(DIFF_RECS, data, TYPE_COLUMN);
866 : 644 : copyColumn(DIFF_RECS, data, TTL_COLUMN);
867 : 644 : copyColumn(DIFF_RECS, data, NAME_COLUMN);
868 : 644 : copyColumn(DIFF_RECS, data, RDATA_COLUMN);
869 : :
870 [ - + ]: 17 : } else if (rc != SQLITE_DONE) {
871 [ # # ][ # # ]: 0 : isc_throw(DataSourceError,
[ # # ][ # # ]
872 : : "Unexpected failure in sqlite3_step: " <<
873 : : sqlite3_errmsg(accessor_->dbparameters_->db_));
874 : : }
875 : 661 : last_status_ = rc;
876 : : }
877 : 665 : return (last_status_ == SQLITE_ROW);
878 : : }
879 : :
880 : : private:
881 : :
882 : : /// \brief Reset prepared statement
883 : : ///
884 : : /// Sets up the statement so that new parameters can be attached to it and
885 : : /// that it can be used to query for another difference sequence.
886 : : ///
887 : : /// \param stindex Index of prepared statement to which to bind
888 : 63 : void reset(int stindex) {
889 : 63 : sqlite3_stmt* stmt = accessor_->dbparameters_->getStatement(stindex);
890 [ + - - + ]: 126 : if ((sqlite3_reset(stmt) != SQLITE_OK) ||
[ - + ]
891 : 63 : (sqlite3_clear_bindings(stmt) != SQLITE_OK)) {
892 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Could not clear statement bindings in '" <<
[ # # ][ # # ]
[ # # ][ # # ]
893 : : text_statements[stindex] << "': " <<
894 : : sqlite3_errmsg(accessor_->dbparameters_->db_));
895 : : }
896 : 63 : }
897 : :
898 : : /// \brief Bind Int
899 : : ///
900 : : /// Binds an integer to a specific variable in a prepared statement.
901 : : ///
902 : : /// \param stindex Index of prepared statement to which to bind
903 : : /// \param varindex Index of variable to which to bind
904 : : /// \param value Value of variable to bind
905 : : /// \exception SQLite3Error on an error
906 : 189 : void bindInt(int stindex, int varindex, sqlite3_int64 value) {
907 [ - + ]: 378 : if (sqlite3_bind_int64(accessor_->dbparameters_->getStatement(stindex),
908 : 189 : varindex, value) != SQLITE_OK) {
909 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Could not bind value to parameter " <<
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
910 : : varindex << " in statement '" <<
911 : : text_statements[stindex] << "': " <<
912 : : sqlite3_errmsg(accessor_->dbparameters_->db_));
913 : : }
914 : 189 : }
915 : :
916 : : ///\brief Get Single Value
917 : : ///
918 : : /// Executes a prepared statement (which has parameters bound to it)
919 : : /// for which the result of a single value is expected.
920 : : ///
921 : : /// \param stindex Index of prepared statement in statement table.
922 : : ///
923 : : /// \return Value of SELECT.
924 : : ///
925 : : /// \exception TooMuchData Multiple rows returned when one expected
926 : : /// \exception TooLittleData Zero rows returned when one expected
927 : : /// \exception DataSourceError SQLite3-related error
928 : 45 : int getSingleValue(StatementID stindex) {
929 : :
930 : : // Get a pointer to the statement for brevity (does not transfer
931 : : // resources)
932 : 45 : sqlite3_stmt* stmt = accessor_->dbparameters_->getStatement(stindex);
933 : :
934 : : // Execute the data. Should be just one result
935 : 45 : int rc = sqlite3_step(stmt);
936 : 45 : int result = -1;
937 [ + + ]: 45 : if (rc == SQLITE_ROW) {
938 : :
939 : : // Got some data, extract the value
940 : 37 : result = sqlite3_column_int(stmt, 0);
941 : 37 : rc = sqlite3_step(stmt);
942 [ + - ]: 37 : if (rc == SQLITE_DONE) {
943 : :
944 : : // All OK, exit with the value.
945 : : return (result);
946 : :
947 [ # # ]: 0 : } else if (rc == SQLITE_ROW) {
948 [ # # ]: 0 : isc_throw(TooMuchData, "request to return one value from "
949 : : "diffs table returned multiple values");
950 : : }
951 [ + - ]: 8 : } else if (rc == SQLITE_DONE) {
952 : :
953 : : // No data in the table. A bare exception with no explanation is
954 : : // thrown, as it will be replaced by a more informative one by
955 : : // the caller.
956 [ + - ]: 16 : isc_throw(TooLittleData, "");
957 : : }
958 : :
959 : : // We get here on an error.
960 [ # # ][ # # ]: 0 : isc_throw(DataSourceError, "could not get data from diffs table: " <<
[ # # ][ # # ]
961 : : sqlite3_errmsg(accessor_->dbparameters_->db_));
962 : :
963 : : // Keep the compiler happy with a return value.
964 : : return (result);
965 : : }
966 : :
967 : : /// \brief Find index
968 : : ///
969 : : /// Executes the prepared statement locating the high or low index in
970 : : /// the diffs table and returns that index.
971 : : ///
972 : : /// \param stmt_id Index of the prepared statement to execute
973 : : /// \param zone_id ID of the zone for which the index is being sought
974 : : /// \param serial Zone serial number for which an index is being sought.
975 : : /// \param diff Code to delete record additions or deletions
976 : : ///
977 : : /// \return int ID of the row in the difss table corresponding to the
978 : : /// statement.
979 : : ///
980 : : /// \exception TooLittleData Internal error, no result returned when one
981 : : /// was expected.
982 : : /// \exception NoSuchSerial Serial number not found.
983 : : /// \exception NoDiffsData No data for this zone found in diffs table
984 : 45 : int findIndex(StatementID stindex, int zone_id, uint32_t serial, int diff) {
985 : :
986 : : // Set up the statement
987 : 45 : reset(stindex);
988 : 45 : bindInt(stindex, 1, zone_id);
989 : 45 : bindInt(stindex, 2, serial);
990 : 45 : bindInt(stindex, 3, diff);
991 : :
992 : : // Execute the statement
993 : 45 : int result = -1;
994 : : try {
995 [ + + ]: 45 : result = getSingleValue(stindex);
996 : :
997 [ - + ]: 16 : } catch (const TooLittleData&) {
998 : :
999 : : // No data returned but the SQL query succeeded. Only possibility
1000 : : // is that there is no entry in the differences table for the given
1001 : : // zone and version.
1002 [ - + ][ - + ]: 24 : isc_throw(NoSuchSerial, "No entry in differences table for" <<
[ - + ][ - + ]
[ - + ]
1003 : : " zone ID " << zone_id << ", serial number " << serial);
1004 : : }
1005 : :
1006 : 37 : return (result);
1007 : : }
1008 : :
1009 : : /// \brief Copy Column to Output
1010 : : ///
1011 : : /// Copies the textual data in the result set to the specified column
1012 : : /// in the output.
1013 : : ///
1014 : : /// \param stindex Index of prepared statement used to access data
1015 : : /// \param data Array of columns passed to getNext
1016 : : /// \param column Column of output to copy
1017 : 2576 : void copyColumn(StatementID stindex, std::string (&data)[COLUMN_COUNT],
1018 : : int column) {
1019 : :
1020 : : // Get a pointer to the statement for brevity (does not transfer
1021 : : // resources)
1022 : 2576 : sqlite3_stmt* stmt = accessor_->dbparameters_->getStatement(stindex);
1023 : : data[column] = convertToPlainChar(sqlite3_column_text(stmt,
1024 : : column),
1025 : 2576 : accessor_->dbparameters_->db_);
1026 : 2576 : }
1027 : :
1028 : : // Attributes
1029 : :
1030 : : boost::shared_ptr<const SQLite3Accessor> accessor_; // Accessor object
1031 : : int last_status_; // Last status received from sqlite3_step
1032 : : };
1033 : :
1034 : : // ... and return the iterator
1035 : :
1036 : : DatabaseAccessor::IteratorContextPtr
1037 : 26 : SQLite3Accessor::getDiffs(int id, uint32_t start, uint32_t end) const {
1038 : : return (IteratorContextPtr(new DiffContext(shared_from_this(), id, start,
1039 [ + - ][ + + ]: 44 : end)));
1040 : : }
1041 : :
1042 : :
1043 : :
1044 : : pair<bool, int>
1045 : 196 : SQLite3Accessor::startUpdateZone(const string& zone_name, const bool replace) {
1046 [ + + ]: 196 : if (dbparameters_->updating_zone) {
1047 [ + - ][ + - ]: 2 : isc_throw(DataSourceError,
1048 : : "duplicate zone update on SQLite3 data source");
1049 : : }
1050 [ + + ]: 195 : if (dbparameters_->in_transaction) {
1051 [ + - ][ + - ]: 2 : isc_throw(DataSourceError,
1052 : : "zone update attempt in another SQLite3 transaction");
1053 : : }
1054 : :
1055 : 194 : const pair<bool, int> zone_info(getZone(zone_name));
1056 [ + + ]: 194 : if (!zone_info.first) {
1057 : 1 : return (zone_info);
1058 : : }
1059 : :
1060 : 193 : StatementProcessor(*dbparameters_, BEGIN,
1061 [ + - ]: 193 : "start an SQLite3 update transaction").exec();
1062 : :
1063 [ + + ]: 193 : if (replace) {
1064 : : // First, clear all current data from tables.
1065 : : typedef pair<StatementID, const char* const> StatementSpec;
1066 : : const StatementSpec delzone_stmts[] =
1067 : : { StatementSpec(DEL_ZONE_RECORDS, "delete zone records"),
1068 : : StatementSpec(DEL_ZONE_NSEC3_RECORDS,
1069 : : "delete zone NSEC3 records") };
1070 : : try {
1071 [ + + ]: 346 : for (size_t i = 0;
1072 : : i < sizeof(delzone_stmts) / sizeof(delzone_stmts[0]);
1073 : : ++i) {
1074 : 231 : StatementProcessor delzone_proc(*dbparameters_,
1075 : : delzone_stmts[i].first,
1076 [ + - ][ + - ]: 462 : delzone_stmts[i].second);
1077 [ + - ]: 231 : delzone_proc.bindInt(1, zone_info.second);
1078 [ + + ]: 231 : delzone_proc.exec();
1079 : : }
1080 [ - + ]: 2 : } catch (const DataSourceError&) {
1081 : : // Once we start a transaction, if something unexpected happens
1082 : : // we need to rollback the transaction so that a subsequent update
1083 : : // is still possible with this accessor.
1084 : 1 : StatementProcessor(*dbparameters_, ROLLBACK,
1085 [ - + ][ - + ]: 1 : "rollback an SQLite3 transaction").exec();
[ - + ]
1086 : 1 : throw;
1087 : : }
1088 : : }
1089 : :
1090 : 192 : dbparameters_->in_transaction = true;
1091 : 192 : dbparameters_->updating_zone = true;
1092 : 192 : dbparameters_->updated_zone_id = zone_info.second;
1093 : 192 : dbparameters_->updated_zone_origin_ = zone_name;
1094 : :
1095 : 193 : return (zone_info);
1096 : : }
1097 : :
1098 : : void
1099 : 30 : SQLite3Accessor::startTransaction() {
1100 [ + + ]: 30 : if (dbparameters_->in_transaction) {
1101 [ + - ][ + - ]: 4 : isc_throw(DataSourceError,
1102 : : "duplicate transaction on SQLite3 data source");
1103 : : }
1104 : :
1105 : 28 : StatementProcessor(*dbparameters_, BEGIN,
1106 [ + - ]: 28 : "start an SQLite3 transaction").exec();
1107 : 28 : dbparameters_->in_transaction = true;
1108 : 28 : }
1109 : :
1110 : : void
1111 : 167 : SQLite3Accessor::commit() {
1112 [ + + ]: 167 : if (!dbparameters_->in_transaction) {
1113 [ + - ][ + - ]: 2 : isc_throw(DataSourceError, "performing commit on SQLite3 "
1114 : : "data source without transaction");
1115 : : }
1116 : :
1117 : 166 : StatementProcessor(*dbparameters_, COMMIT,
1118 [ + + ]: 166 : "commit an SQLite3 transaction").exec();
1119 : 163 : dbparameters_->in_transaction = false;
1120 : 163 : dbparameters_->updating_zone = false;
1121 : 163 : dbparameters_->updated_zone_id = -1;
1122 : 163 : dbparameters_->updated_zone_origin_.clear();
1123 : 163 : }
1124 : :
1125 : : void
1126 : 47 : SQLite3Accessor::rollback() {
1127 [ + + ]: 47 : if (!dbparameters_->in_transaction) {
1128 [ + - ][ + - ]: 2 : isc_throw(DataSourceError, "performing rollback on SQLite3 "
1129 : : "data source without transaction");
1130 : : }
1131 : :
1132 : 46 : StatementProcessor(*dbparameters_, ROLLBACK,
1133 [ + + ]: 46 : "rollback an SQLite3 transaction").exec();
1134 : 45 : dbparameters_->in_transaction = false;
1135 : 45 : dbparameters_->updating_zone = false;
1136 : 45 : dbparameters_->updated_zone_id = -1;
1137 : 45 : dbparameters_->updated_zone_origin_.clear();
1138 : 45 : }
1139 : :
1140 : : namespace {
1141 : : // Commonly used code sequence for adding/deleting record
1142 : : template <typename COLUMNS_TYPE>
1143 : : void
1144 : 440 : doUpdate(SQLite3Parameters& dbparams, StatementID stmt_id,
1145 : : COLUMNS_TYPE update_params, const char* exec_desc)
1146 : : {
1147 [ + - ][ + - ]: 14832 : StatementProcessor proc(dbparams, stmt_id, exec_desc);
1148 : :
1149 : 7416 : int param_id = 0;
1150 [ + - ][ + - ]: 7416 : proc.bindInt(++param_id, dbparams.updated_zone_id);
[ + - ]
1151 : : const size_t column_count =
1152 : : sizeof(update_params) / sizeof(update_params[0]);
1153 [ + + ][ + + ]: 50581 : for (int i = 0; i < column_count; ++i) {
[ + + ]
1154 : : // The old sqlite3 data source API assumes NULL for an empty column.
1155 : : // We need to provide compatibility at least for now.
1156 [ + - ][ + - ]: 79520 : proc.bindText(++param_id, update_params[i].empty() ? NULL :
[ + - ][ + - ]
[ + + ][ + - ]
1157 : : update_params[i].c_str(), SQLITE_TRANSIENT);
1158 : : }
1159 [ + - ][ + - ]: 7416 : proc.exec();
[ + - ]
1160 : 440 : }
1161 : : }
1162 : :
1163 : : void
1164 : 6966 : SQLite3Accessor::addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) {
1165 [ + + ]: 6966 : if (!dbparameters_->updating_zone) {
1166 [ + - ][ + - ]: 2 : isc_throw(DataSourceError, "adding record to SQLite3 "
1167 : : "data source without transaction");
1168 : : }
1169 : : doUpdate<const string (&)[ADD_COLUMN_COUNT]>(
1170 : 6965 : *dbparameters_, ADD_RECORD, columns, "add record to zone");
1171 : 6965 : }
1172 : :
1173 : : void
1174 : 12 : SQLite3Accessor::addNSEC3RecordToZone(
1175 : : const string (&columns)[ADD_NSEC3_COLUMN_COUNT])
1176 : : {
1177 [ + + ]: 12 : if (!dbparameters_->updating_zone) {
1178 [ + - ][ + - ]: 2 : isc_throw(DataSourceError, "adding NSEC3-related record to SQLite3 "
1179 : : "data source without transaction");
1180 : : }
1181 : :
1182 : : // XXX: the current implementation of SQLite3 schema requires the 'owner'
1183 : : // column, and the current implementation of getAllRecords() relies on it,
1184 : : // while the addNSEC3RecordToZone interface doesn't provide it explicitly.
1185 : : // We should revisit it at the design level, but for now we internally
1186 : : // convert the given parameter to satisfy the internal requirements.
1187 : : const string sqlite3_columns[ADD_NSEC3_COLUMN_COUNT + 1] =
1188 : : { columns[ADD_NSEC3_HASH],
1189 : 22 : columns[ADD_NSEC3_HASH] + "." + dbparameters_->updated_zone_origin_,
1190 : : columns[ADD_NSEC3_TTL],
1191 [ + - ]: 88 : columns[ADD_NSEC3_TYPE], columns[ADD_NSEC3_RDATA] };
[ + - + - ]
[ + - ][ + - ]
[ # # ]
1192 : : doUpdate<const string (&)[ADD_NSEC3_COLUMN_COUNT + 1]>(
1193 : 11 : *dbparameters_, ADD_NSEC3_RECORD, sqlite3_columns,
1194 [ + + ][ # # ]: 66 : "add NSEC3 record to zone");
1195 : 11 : }
1196 : :
1197 : : void
1198 : 437 : SQLite3Accessor::deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) {
1199 [ + + ]: 437 : if (!dbparameters_->updating_zone) {
1200 [ + - ][ + - ]: 2 : isc_throw(DataSourceError, "deleting record in SQLite3 "
1201 : : "data source without transaction");
1202 : : }
1203 : : doUpdate<const string (&)[DEL_PARAM_COUNT]>(
1204 : 436 : *dbparameters_, DEL_RECORD, params, "delete record from zone");
1205 : 436 : }
1206 : :
1207 : : void
1208 : 5 : SQLite3Accessor::deleteNSEC3RecordInZone(
1209 : : const string (¶ms)[DEL_PARAM_COUNT])
1210 : : {
1211 [ + + ]: 5 : if (!dbparameters_->updating_zone) {
1212 [ + - ][ + - ]: 2 : isc_throw(DataSourceError, "deleting NSEC3-related record in SQLite3 "
1213 : : "data source without transaction");
1214 : : }
1215 : : doUpdate<const string (&)[DEL_PARAM_COUNT]>(
1216 : 4 : *dbparameters_, DEL_NSEC3_RECORD, params,
1217 : 4 : "delete NSEC3 record from zone");
1218 : 4 : }
1219 : :
1220 : : void
1221 : 839 : SQLite3Accessor::addRecordDiff(int zone_id, uint32_t serial,
1222 : : DiffOperation operation,
1223 : : const std::string (¶ms)[DIFF_PARAM_COUNT])
1224 : : {
1225 [ + + ]: 839 : if (!dbparameters_->updating_zone) {
1226 [ + - ][ + - ]: 4 : isc_throw(DataSourceError, "adding record diff without update "
[ + - ][ + - ]
1227 : : "transaction on " << getDBName());
1228 : : }
1229 [ + + ]: 837 : if (zone_id != dbparameters_->updated_zone_id) {
1230 [ + - ][ + - ]: 2 : isc_throw(DataSourceError, "bad zone ID for adding record diff on "
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
1231 : : << getDBName() << ": " << zone_id << ", must be "
1232 : : << dbparameters_->updated_zone_id);
1233 : : }
1234 : :
1235 : 836 : StatementProcessor proc(*dbparameters_, ADD_RECORD_DIFF,
1236 : 1672 : "add record diff");
1237 : 836 : int param_id = 0;
1238 [ + - ]: 836 : proc.bindInt(++param_id, zone_id);
1239 [ + - ]: 836 : proc.bindInt64(++param_id, serial);
1240 [ + - ]: 836 : proc.bindInt(++param_id, operation);
1241 [ + + ]: 4180 : for (int i = 0; i < DIFF_PARAM_COUNT; ++i) {
1242 [ + - ]: 3344 : proc.bindText(++param_id, params[i].c_str(), SQLITE_TRANSIENT);
1243 : : }
1244 [ + - ]: 836 : proc.exec();
1245 : 836 : }
1246 : :
1247 : : std::string
1248 : 32 : SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname)
1249 : : const
1250 : : {
1251 : 32 : sqlite3_stmt* const stmt = dbparameters_->getStatement(FIND_PREVIOUS);
1252 : 32 : sqlite3_reset(stmt);
1253 : 32 : sqlite3_clear_bindings(stmt);
1254 : :
1255 [ - + ]: 32 : if (sqlite3_bind_int(stmt, 1, zone_id) != SQLITE_OK) {
1256 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id <<
[ # # ][ # # ]
[ # # ][ # # ]
1257 : : " to SQL statement (find previous): " <<
1258 : : sqlite3_errmsg(dbparameters_->db_));
1259 : : }
1260 [ - + ]: 32 : if (sqlite3_bind_text(stmt, 2, rname.c_str(), -1, SQLITE_STATIC) !=
1261 : : SQLITE_OK) {
1262 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Could not bind name " << rname <<
[ # # ][ # # ]
[ # # ][ # # ]
1263 : : " to SQL statement (find previous): " <<
1264 : : sqlite3_errmsg(dbparameters_->db_));
1265 : : }
1266 : :
1267 : 5 : std::string result;
1268 [ + - ]: 32 : const int rc = sqlite3_step(stmt);
1269 [ + + ]: 32 : if (rc == SQLITE_ROW) {
1270 : : // We found it
1271 : : result = convertToPlainChar(sqlite3_column_text(stmt, 0),
1272 [ + - ][ + - ]: 27 : dbparameters_->db_);
1273 : : }
1274 [ + - ]: 32 : sqlite3_reset(stmt);
1275 : :
1276 [ + + ]: 32 : if (rc == SQLITE_DONE) {
1277 : : // No NSEC records here, this DB doesn't support DNSSEC or
1278 : : // we asked before the apex
1279 [ + - ][ + - ]: 10 : isc_throw(isc::NotImplemented, "The zone doesn't support DNSSEC or "
1280 : : "query before apex");
1281 : : }
1282 : :
1283 [ - + ]: 27 : if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
1284 : : // Some kind of error
1285 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Could not get data for previous name");
[ # # ]
1286 : : }
1287 : :
1288 : 27 : return (result);
1289 : : }
1290 : :
1291 : : std::string
1292 : 6 : SQLite3Accessor::findPreviousNSEC3Hash(int zone_id, const std::string& hash)
1293 : : const
1294 : : {
1295 : 6 : sqlite3_stmt* const stmt = dbparameters_->getStatement(NSEC3_PREVIOUS);
1296 : 6 : sqlite3_reset(stmt);
1297 : 6 : sqlite3_clear_bindings(stmt);
1298 : :
1299 [ - + ]: 6 : if (sqlite3_bind_int(stmt, 1, zone_id) != SQLITE_OK) {
1300 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id <<
[ # # ][ # # ]
[ # # ][ # # ]
1301 : : " to SQL statement (find previous NSEC3): " <<
1302 : : sqlite3_errmsg(dbparameters_->db_));
1303 : : }
1304 [ - + ]: 6 : if (sqlite3_bind_text(stmt, 2, hash.c_str(), -1, SQLITE_STATIC) !=
1305 : : SQLITE_OK) {
1306 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Could not bind hash " << hash <<
[ # # ][ # # ]
[ # # ][ # # ]
1307 : : " to SQL statement (find previous NSEC3): " <<
1308 : : sqlite3_errmsg(dbparameters_->db_));
1309 : : }
1310 : :
1311 : 1 : std::string result;
1312 [ + - ]: 6 : const int rc = sqlite3_step(stmt);
1313 [ + + ]: 6 : if (rc == SQLITE_ROW) {
1314 : : // We found it
1315 : : result = convertToPlainChar(sqlite3_column_text(stmt, 0),
1316 [ + - ][ + - ]: 3 : dbparameters_->db_);
1317 : : }
1318 [ + - ]: 6 : sqlite3_reset(stmt);
1319 : :
1320 [ - + ]: 6 : if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
1321 : : // Some kind of error
1322 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Could not get data for previous hash");
[ # # ]
1323 : : }
1324 : :
1325 [ + + ]: 6 : if (rc == SQLITE_DONE) {
1326 : : // No NSEC3 records before this hash. This means we should wrap
1327 : : // around and take the last one.
1328 [ + - ]: 3 : sqlite3_stmt* const stmt = dbparameters_->getStatement(NSEC3_LAST);
1329 [ + - ]: 3 : sqlite3_reset(stmt);
1330 [ + - ]: 3 : sqlite3_clear_bindings(stmt);
1331 : :
1332 [ + - ][ - + ]: 3 : if (sqlite3_bind_int(stmt, 1, zone_id) != SQLITE_OK) {
1333 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id <<
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
1334 : : " to SQL statement (find last NSEC3): " <<
1335 : : sqlite3_errmsg(dbparameters_->db_));
1336 : : }
1337 : :
1338 [ + - ]: 3 : const int rc = sqlite3_step(stmt);
1339 [ + + ]: 3 : if (rc == SQLITE_ROW) {
1340 : : // We found it
1341 : : result = convertToPlainChar(sqlite3_column_text(stmt, 0),
1342 [ + - ][ + - ]: 2 : dbparameters_->db_);
1343 : : }
1344 [ + - ]: 3 : sqlite3_reset(stmt);
1345 : :
1346 [ - + ]: 3 : if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
1347 : : // Some kind of error
1348 [ # # ][ # # ]: 0 : isc_throw(SQLite3Error, "Could not get data for last hash");
[ # # ]
1349 : : }
1350 : :
1351 [ + + ]: 3 : if (rc == SQLITE_DONE) {
1352 : : // No NSEC3 at all in the zone. Well, bad luck, but you should not
1353 : : // have asked in the first place.
1354 [ + - ][ + - ]: 2 : isc_throw(DataSourceError, "No NSEC3 in this zone");
[ + - ]
1355 : : }
1356 : : }
1357 : :
1358 : 5 : return (result);
1359 : : }
1360 : :
1361 : : } // end of namespace datasrc
1362 : 541 : } // end of namespace isc
|