// Copyright (C) 2012 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. #include #include #include #include #include #include #include #include "sqlite_ubench.h" using namespace std; SQLite_uBenchmark::SQLite_uBenchmark(const string& filename, uint32_t num_iterations, bool sync, bool verbose) :uBenchmark(num_iterations, filename, sync, verbose), db_(NULL) { } void SQLite_uBenchmark::connect() { int result = sqlite3_open(dbname_.c_str(), &db_); if (result != SQLITE_OK) { failure("Failed to open DB file"); } result = sqlite3_exec(db_, "DELETE FROM lease4", NULL, NULL, NULL); if (result != SQLITE_OK) { failure("Failed to delete old entries"); } if (sync_) { sqlite3_exec(db_, "PRAGMA synchronous = ON", NULL, NULL, NULL); } else { sqlite3_exec(db_, "PRAGMA synchronous = OFF", NULL, NULL, NULL); } // see http://www.sqlite.org/pragma.html#pragma_journal_mode // for detailed explanation. Available modes: DELETE, TRUNCATE, // PERSIST, MEMORY, WAL, OFF sqlite3_exec(db_, "PRAGMA journal_mode = OFF", NULL, NULL, NULL); } void SQLite_uBenchmark::disconnect() { if (db_) { sqlite3_close(db_); db_ = NULL; } else { throw "Can't close SQLite connection: it was never open."; } } void SQLite_uBenchmark::createLease4Test() { if (!db_) { throw "SQLite connection is closed."; } uint32_t addr = BASE_ADDR4; // Let's start with 1.0.0.0 address const uint8_t hwaddr_len = 20; // Not a real field char hwaddr[hwaddr_len]; const uint8_t client_id_len = 128; char client_id[client_id_len]; uint32_t valid_lft = 1000; // We can use the same value for all leases uint32_t recycle_time = 0; // Not supported in any foresable future, // so keep this as 0 char cltt[48]; // Timestamp uint32_t pool_id = 0; // Let's use pools 0-99 bool fixed = false; string hostname("foo"); // Will generate it dynamically bool fqdn_fwd = true; // Let's pretend to do AAAA update bool fqdn_rev = true; // Let's pretend to do PTR update cout << "CREATE: "; for (uint8_t i = 0; i < hwaddr_len; i++) { hwaddr[i] = 65 + i; } hwaddr[19] = 0; // workaround for (uint8_t i = 0; i < client_id_len; i++) { client_id[i] = 33 + i; } client_id[6] = 'X'; // there's apostrophe here. It would confuse // query formatting, let's get rid of it client_id[127] = 0; // workaround sqlite3_stmt *stmt = NULL; if (compiled_stmt_) { char query[] = "INSERT INTO lease4(addr,hwaddr,client_id," "valid_lft,recycle_time,cltt,pool_id,fixed,hostname," "fqdn_fwd,fqdn_rev) VALUES(?001,?002,?003,?004,?005,?006,?007,?008,?009,?010,?011);"; int result = sqlite3_prepare_v2(db_, query, strlen(query), &stmt, NULL); if (result != SQLITE_OK) { failure("Failed to compile statement"); } } for (uint32_t i = 0; i < num_; i++) { sprintf(cltt, "2012-07-11 15:43:%02d", i % 60); addr++; char* errorMsg = NULL; if (!compiled_stmt_) { // the first address is 1.0.0.0. char query[2000]; /// @todo: Encode HWADDR and CLIENT-ID properly sprintf(query, "INSERT INTO lease4(addr,hwaddr,client_id," "valid_lft,recycle_time,cltt,pool_id,fixed,hostname," "fqdn_fwd,fqdn_rev) VALUES(%u,'%s','%s',%d,%d,'%s',%d,'%s','%s','%s','%s');", addr, hwaddr, client_id, valid_lft, recycle_time, cltt, pool_id, (fixed?"true":"false"), hostname.c_str(), (fqdn_fwd?"true":"false"), (fqdn_rev?"true":"false")); int result = sqlite3_exec(db_, query, NULL, 0, &errorMsg); if (result != SQLITE_OK) { stringstream tmp; tmp << "INSERT error:" << errorMsg; failure(tmp.str().c_str()); } } else { // compiled statement int result = sqlite3_bind_int(stmt, 1, addr); if (result != SQLITE_OK) { failure("sqlite3_bind_int() for column 1"); } result = sqlite3_bind_blob(stmt, 2, hwaddr, hwaddr_len, NULL); if (result != SQLITE_OK) { failure("sqlite3_bind_blob() for column 2"); } result = sqlite3_bind_blob(stmt, 3, client_id, client_id_len, NULL); if (result != SQLITE_OK) { failure("sqlite3_bind_blob() for column 3"); } if (sqlite3_bind_int(stmt, 4, valid_lft) != SQLITE_OK) { failure("sqlite3_bind_int() for column 4"); } if (sqlite3_bind_int(stmt, 5, recycle_time) != SQLITE_OK) { failure("sqlite3_bind_int() for column 5"); } if (sqlite3_bind_text(stmt, 6, cltt, strlen(cltt), NULL) != SQLITE_OK) { failure("sqlite3_bind_int() for column 6"); } if (sqlite3_bind_int(stmt, 7, pool_id) != SQLITE_OK) { failure("sqlite3_bind_int() for column 7"); } if (sqlite3_bind_int(stmt, 7, pool_id) != SQLITE_OK) { failure("sqlite3_bind_int() for column 7"); } if (sqlite3_bind_int(stmt, 8, fixed) != SQLITE_OK) { failure("sqlite3_bind_int() for column 8"); } if (sqlite3_bind_text(stmt, 9, hostname.c_str(), hostname.length(), NULL) != SQLITE_OK) { failure("sqlite3_bind_int() for column 9"); } if (sqlite3_bind_int(stmt, 10, fqdn_fwd) != SQLITE_OK) { failure("sqlite3_bind_int() for column 10"); } if (sqlite3_bind_int(stmt, 11, fqdn_rev) != SQLITE_OK) { failure("sqlite3_bind_int() for column 11"); } result = sqlite3_step(stmt); if (result != SQLITE_DONE) { failure("Failed to execute INSERT clause"); } // let's reset the compiled statement, so it can be used in the // next iteration result = sqlite3_reset(stmt); if (result != SQLITE_OK) { failure("Failed to execute sqlite3_reset()"); } } if (verbose_) { cout << "."; } } if (compiled_stmt_) { int result = sqlite3_finalize(stmt); if (result != SQLITE_OK) { failure("sqlite3_finalize() failed"); } } cout << endl; } static int search_callback(void *counter, int argc, char** argv, char** azColName){ int* cnt = static_cast(counter); (*cnt)++; char buf[512]; // retrieved lease can be accessed here for(int i = 0; i < argc; i++){ // pretend we do something with returned lease if (argv[i]) { strncpy(buf, azColName[i], 512); strncpy(buf, argv[i], 512); } // Uncomment this to print out all contents // cout << azColName[i] << "=" << (argv[i] ? argv[i] : "NULL") << endl; } return (0); } void SQLite_uBenchmark::searchLease4Test() { if (!db_) { throw "SQLite connection is closed."; } cout << "RETRIEVE: "; sqlite3_stmt *stmt = NULL; if (compiled_stmt_) { const char query[] = "SELECT lease_id,addr,hwaddr,client_id,valid_lft," "cltt,pool_id,fixed,hostname,fqdn_fwd,fqdn_rev " "FROM lease4 where addr=?1"; int result = sqlite3_prepare_v2(db_, query, strlen(query), &stmt, NULL); if (result != SQLITE_OK) { failure("Failed to compile statement"); } } for (uint32_t i = 0; i < num_; i++) { uint32_t addr = BASE_ADDR4 + random() % int(num_ / hitratio_); int cnt = 0; if (!compiled_stmt_) { char* errorMsg = NULL; char query[512]; sprintf(query, "SELECT lease_id,addr,hwaddr,client_id,valid_lft," "cltt,pool_id,fixed,hostname,fqdn_fwd,fqdn_rev " "FROM lease4 where addr=%d", addr); int result = sqlite3_exec(db_, query, search_callback, &cnt, &errorMsg); if (result != SQLITE_OK) { stringstream tmp; tmp << "SELECT failed: " << errorMsg; failure(tmp.str().c_str()); } } else { // compiled statement int result = sqlite3_bind_int(stmt, 1, addr); if (result != SQLITE_OK) { failure("sqlite3_bind_int() for column 1"); } result = sqlite3_step(stmt); switch (result) { case SQLITE_ROW: { uint32_t lease_addr = sqlite3_column_int(stmt, 1); const void * lease_hwaddr = sqlite3_column_blob(stmt, 2); uint32_t lease_hwaddr_len = sqlite3_column_bytes(stmt, 2); const void * lease_clientid = sqlite3_column_blob(stmt, 3); uint32_t lease_clientid_len = sqlite3_column_bytes(stmt, 3); uint32_t lease_valid_lft = sqlite3_column_int(stmt, 4); // cltt const unsigned char *lease_cltt = sqlite3_column_text(stmt, 5); uint32_t lease_pool_id = sqlite3_column_int(stmt, 6); uint32_t lease_fixed = sqlite3_column_int(stmt, 7); const unsigned char *lease_hostname = sqlite3_column_text(stmt, 8); uint32_t lease_fqdn_fwd = sqlite3_column_int(stmt, 9); uint32_t lease_fqdn_rev = sqlite3_column_int(stmt, 10); if (lease_addr || lease_hwaddr || lease_hwaddr_len || lease_clientid || lease_clientid_len || lease_valid_lft || lease_cltt || lease_pool_id || lease_fixed || lease_hostname || lease_fqdn_fwd || lease_fqdn_rev) { // we don't need this information, we just want to obtain it to measure // the overhead. That strange if is only to quell compiler/cppcheck // warning about unused variables. cnt = 1; } cnt = 1; // there is at least one row break; } case SQLITE_DONE: cnt = 0; // there are no rows at all (i.e. no such lease) break; default: failure("Failed to execute SELECT clause"); } // let's reset the compiled statement, so it can be used in the // next iteration result = sqlite3_reset(stmt); if (result != SQLITE_OK) { failure("Failed to execute sqlite3_reset()"); } } if (verbose_) { cout << (cnt?".":"X"); } } if (compiled_stmt_) { int result = sqlite3_finalize(stmt); if (result != SQLITE_OK) { failure("sqlite3_finalize() failed"); } } cout << endl; } void SQLite_uBenchmark::updateLease4Test() { if (!db_) { throw "SQLite connection is closed."; } cout << "UPDATE: "; sqlite3_stmt *stmt = NULL; if (compiled_stmt_) { const char query[] = "UPDATE lease4 SET valid_lft=1002, cltt='now' WHERE addr=?1"; int result = sqlite3_prepare_v2(db_, query, strlen(query), &stmt, NULL); if (result != SQLITE_OK) { failure("Failed to compile statement"); } } for (uint32_t i = 0; i < num_; i++) { uint32_t addr = BASE_ADDR4 + random() % num_; if (!compiled_stmt_) { char* errorMsg = NULL; char query[512]; sprintf(query, "UPDATE lease4 SET valid_lft=1002, cltt='now' WHERE addr=%d", addr); int result = sqlite3_exec(db_, query, NULL /* no callback here*/, 0, &errorMsg); if (result != SQLITE_OK) { stringstream tmp; tmp << "UPDATE error:" << errorMsg; failure(tmp.str().c_str()); } } else { int result = sqlite3_bind_int(stmt, 1, addr); if (result != SQLITE_OK) { failure("sqlite3_bind_int() for column 1"); } result = sqlite3_step(stmt); if (result != SQLITE_OK && result != SQLITE_DONE) { failure("Failed to execute sqlite3_step() for UPDATE"); } // let's reset the compiled statement, so it can be used in the // next iteration result = sqlite3_reset(stmt); if (result != SQLITE_OK) { failure("Failed to execute sqlite3_reset()"); } } if (verbose_) { cout << "."; } } if (compiled_stmt_) { int result = sqlite3_finalize(stmt); if (result != SQLITE_OK) { failure("sqlite3_finalize() failed"); } } cout << endl; } void SQLite_uBenchmark::deleteLease4Test() { if (!db_) { throw "SQLite connection is closed."; } cout << "DELETE: "; sqlite3_stmt *stmt = NULL; if (compiled_stmt_) { const char query[] = "DELETE FROM lease4 WHERE addr=?1"; int result = sqlite3_prepare_v2(db_, query, strlen(query), &stmt, NULL); if (result != SQLITE_OK) { failure("Failed to compile statement"); } } for (uint32_t i = 0; i < num_; i++) { uint32_t addr = BASE_ADDR4 + i; if (!compiled_stmt_) { char* errorMsg = NULL; char query[2000]; sprintf(query, "DELETE FROM lease4 WHERE addr=%d", addr); int result = sqlite3_exec(db_, query, NULL /* no callback here*/, 0, &errorMsg); if (result != SQLITE_OK) { stringstream tmp; tmp << "DELETE error:" << errorMsg; failure(tmp.str().c_str()); } } else { // compiled statement int result = sqlite3_bind_int(stmt, 1, addr); if (result != SQLITE_OK) { failure("sqlite3_bind_int() for column 1"); } result = sqlite3_step(stmt); if (result != SQLITE_OK && result != SQLITE_DONE) { failure("Failed to execute sqlite3_step() for UPDATE"); } // let's reset the compiled statement, so it can be used in the // next iteration result = sqlite3_reset(stmt); if (result != SQLITE_OK) { failure("Failed to execute sqlite3_reset()"); } } if (verbose_) { cout << "."; } } if (compiled_stmt_) { int result = sqlite3_finalize(stmt); if (result != SQLITE_OK) { failure("sqlite3_finalize() failed"); } } cout << endl; } void SQLite_uBenchmark::printInfo() { cout << "SQLite version is " << sqlite3_libversion() << "sourceid version is " << sqlite3_sourceid() << endl; } int main(int argc, char* const argv[]) { const char* filename = "sqlite.db"; uint32_t num = 100; bool sync = true; bool verbose = true; SQLite_uBenchmark bench(filename, num, sync, verbose); bench.parseCmdline(argc, argv); int result = bench.run(); return (result); }