Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Sebastian Schrader
Kea
Commits
1351cb42
Commit
1351cb42
authored
Aug 12, 2011
by
JINMEI Tatuya
Browse files
[1068] update (write) support in the SQLite3 accessor.
It includes refactoring/cleanups on managing SQLite3 statements.
parent
575bd3ec
Changes
4
Hide whitespace changes
Inline
Side-by-side
src/lib/datasrc/sqlite3_accessor.cc
View file @
1351cb42
...
...
@@ -14,36 +14,104 @@
#include <sqlite3.h>
#include <string>
#include <vector>
#include <boost/foreach.hpp>
#include <datasrc/sqlite3_accessor.h>
#include <datasrc/logger.h>
#include <datasrc/data_source.h>
#include <util/filename.h>
using
namespace
std
;
namespace
isc
{
namespace
datasrc
{
// 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
,
BEGIN
=
2
,
COMMIT
=
3
,
ROLLBACK
=
4
,
DEL_ZONE_RECORDS
=
5
,
ADD_RECORD
=
6
,
DEL_RECORD
=
7
,
NUM_STATEMENTS
=
8
};
const
char
*
const
text_statements
[
NUM_STATEMENTS
]
=
{
"SELECT id FROM zones WHERE name=?1 AND rdclass = ?2"
,
// ZONE
"SELECT rdtype, ttl, sigtype, rdata FROM records "
// ANY
"WHERE zone_id=?1 AND name=?2"
,
"BEGIN"
,
// BEGIN
"COMMIT"
,
// COMMIT
"ROLLBACK"
,
// ROLLBACK
"DELETE FROM records WHERE zone_id=?1"
,
// DEL_ZONE_RECORDS
"INSERT INTO records "
// ADD_RECORD
"(zone_id, name, rname, ttl, rdtype, sigtype, rdata) "
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"
,
"DELETE FROM records WHERE zone_id=?1 AND name=?2 "
// DEL_RECORD
"AND rdtype=?3 AND rdata=?4"
};
struct
SQLite3Parameters
{
SQLite3Parameters
()
:
db_
(
NULL
),
version_
(
-
1
),
q_zone_
(
NULL
),
q_any_
(
NULL
)
/*q_record_(NULL), q_addrs_(NULL), q_referral_(NULL),
q_count_(NULL), q_previous_(NULL), q_nsec3_(
NULL
),
q_prevnsec3_(NULL) */
{
}
db_
(
NULL
),
version_
(
-
1
),
updating_zone
(
false
),
updated_zone_id
(
-
1
)
{
for
(
int
i
=
0
;
i
<
NUM_STATEMENTS
;
++
i
)
{
statements_
[
i
]
=
NULL
;
}
}
sqlite3
*
db_
;
int
version_
;
sqlite3_stmt
*
q_zone_
;
sqlite3_stmt
*
q_any_
;
/*
TODO: Yet unneeded statements
sqlite3_stmt* q_record_;
sqlite3_stmt* q_addrs_;
sqlite3_stmt* q_referral_;
sqlite3_stmt* q_count_;
sqlite3_stmt* q_previous_;
sqlite3_stmt* q_nsec3_;
sqlite3_stmt* q_prevnsec3_;
*/
sqlite3_stmt
*
statements_
[
NUM_STATEMENTS
];
bool
updating_zone
;
// whether or not updating the zone
int
updated_zone_id
;
// valid only when updating_zone is true
};
// 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.
class
StatementExecuter
{
public:
// desc will be used on failure in the what() message of the resulting
// DataSourceError exception.
StatementExecuter
(
SQLite3Parameters
*
dbparameters
,
StatementID
stmt_id
,
const
char
*
desc
)
:
dbparameters_
(
dbparameters
),
stmt_id_
(
stmt_id
),
desc_
(
desc
)
{
sqlite3_clear_bindings
(
dbparameters_
->
statements_
[
stmt_id_
]);
}
~
StatementExecuter
()
{
sqlite3_reset
(
dbparameters_
->
statements_
[
stmt_id_
]);
}
void
exec
()
{
if
(
sqlite3_step
(
dbparameters_
->
statements_
[
stmt_id_
])
!=
SQLITE_DONE
)
{
sqlite3_reset
(
dbparameters_
->
statements_
[
stmt_id_
]);
isc_throw
(
DataSourceError
,
"failed to "
<<
desc_
<<
": "
<<
sqlite3_errmsg
(
dbparameters_
->
db_
));
}
}
private:
SQLite3Parameters
*
dbparameters_
;
const
StatementID
stmt_id_
;
const
char
*
const
desc_
;
};
SQLite3Database
::
SQLite3Database
(
const
std
::
string
&
filename
,
...
...
@@ -70,35 +138,10 @@ namespace {
class
Initializer
{
public:
~
Initializer
()
{
if
(
params_
.
q_zone_
!=
NULL
)
{
sqlite3_finalize
(
params_
.
q_zone_
);
}
if
(
params_
.
q_any_
!=
NULL
)
{
sqlite3_finalize
(
params_
.
q_any_
);
}
/*
if (params_.q_record_ != NULL) {
sqlite3_finalize(params_.q_record_);
}
if (params_.q_addrs_ != NULL) {
sqlite3_finalize(params_.q_addrs_);
}
if (params_.q_referral_ != NULL) {
sqlite3_finalize(params_.q_referral_);
}
if (params_.q_count_ != NULL) {
sqlite3_finalize(params_.q_count_);
}
if (params_.q_previous_ != NULL) {
sqlite3_finalize(params_.q_previous_);
for
(
int
i
=
0
;
i
<
NUM_STATEMENTS
;
++
i
)
{
sqlite3_finalize
(
params_
.
statements_
[
i
]);
}
if (params_.q_nsec3_ != NULL) {
sqlite3_finalize(params_.q_nsec3_);
}
if (params_.q_prevnsec3_ != NULL) {
sqlite3_finalize(params_.q_prevnsec3_);
}
*/
if
(
params_
.
db_
!=
NULL
)
{
sqlite3_close
(
params_
.
db_
);
}
...
...
@@ -134,41 +177,6 @@ const char* const SCHEMA_LIST[] = {
NULL
};
const
char
*
const
q_zone_str
=
"SELECT id FROM zones WHERE name=?1 AND rdclass = ?2"
;
const
char
*
const
q_any_str
=
"SELECT rdtype, ttl, sigtype, rdata "
"FROM records WHERE zone_id=?1 AND name=?2"
;
/* TODO: Prune the statements, not everything will be needed maybe?
const char* const q_record_str = "SELECT rdtype, ttl, sigtype, rdata "
"FROM records WHERE zone_id=?1 AND name=?2 AND "
"((rdtype=?3 OR sigtype=?3) OR "
"(rdtype='CNAME' OR sigtype='CNAME') OR "
"(rdtype='NS' OR sigtype='NS'))";
const char* const q_addrs_str = "SELECT rdtype, ttl, sigtype, rdata "
"FROM records WHERE zone_id=?1 AND name=?2 AND "
"(rdtype='A' OR sigtype='A' OR rdtype='AAAA' OR sigtype='AAAA')";
const char* const q_referral_str = "SELECT rdtype, ttl, sigtype, rdata FROM "
"records WHERE zone_id=?1 AND name=?2 AND"
"(rdtype='NS' OR sigtype='NS' OR rdtype='DS' OR sigtype='DS' OR "
"rdtype='DNAME' OR sigtype='DNAME')";
const char* const q_count_str = "SELECT COUNT(*) FROM records "
"WHERE zone_id=?1 AND rname LIKE (?2 || '%');";
const char* const q_previous_str = "SELECT name FROM records "
"WHERE zone_id=?1 AND rdtype = 'NSEC' AND "
"rname < $2 ORDER BY rname DESC LIMIT 1";
const char* const q_nsec3_str = "SELECT rdtype, ttl, rdata FROM nsec3 "
"WHERE zone_id = ?1 AND hash = $2";
const char* const q_prevnsec3_str = "SELECT hash FROM nsec3 "
"WHERE zone_id = ?1 AND hash <= $2 ORDER BY hash DESC LIMIT 1";
*/
sqlite3_stmt
*
prepare
(
sqlite3
*
const
db
,
const
char
*
const
statement
)
{
sqlite3_stmt
*
prepared
=
NULL
;
...
...
@@ -203,17 +211,9 @@ checkAndSetupSchema(Initializer* initializer) {
}
}
initializer
->
params_
.
q_zone_
=
prepare
(
db
,
q_zone_str
);
initializer
->
params_
.
q_any_
=
prepare
(
db
,
q_any_str
);
/* TODO: Yet unneeded statements
initializer->params_.q_record_ = prepare(db, q_record_str);
initializer->params_.q_addrs_ = prepare(db, q_addrs_str);
initializer->params_.q_referral_ = prepare(db, q_referral_str);
initializer->params_.q_count_ = prepare(db, q_count_str);
initializer->params_.q_previous_ = prepare(db, q_previous_str);
initializer->params_.q_nsec3_ = prepare(db, q_nsec3_str);
initializer->params_.q_prevnsec3_ = prepare(db, q_prevnsec3_str);
*/
for
(
int
i
=
0
;
i
<
NUM_STATEMENTS
;
++
i
)
{
initializer
->
params_
.
statements_
[
i
]
=
prepare
(
db
,
text_statements
[
i
]);
}
}
}
...
...
@@ -253,34 +253,10 @@ SQLite3Database::close(void) {
}
// XXX: sqlite3_finalize() could fail. What should we do in that case?
sqlite3_finalize
(
dbparameters_
->
q_zone_
);
dbparameters_
->
q_zone_
=
NULL
;
sqlite3_finalize
(
dbparameters_
->
q_any_
);
dbparameters_
->
q_any_
=
NULL
;
/* TODO: Once they are needed or not, uncomment or drop
sqlite3_finalize(dbparameters->q_record_);
dbparameters->q_record_ = NULL;
sqlite3_finalize(dbparameters->q_addrs_);
dbparameters->q_addrs_ = NULL;
sqlite3_finalize(dbparameters->q_referral_);
dbparameters->q_referral_ = NULL;
sqlite3_finalize(dbparameters->q_count_);
dbparameters->q_count_ = NULL;
sqlite3_finalize(dbparameters->q_previous_);
dbparameters->q_previous_ = NULL;
sqlite3_finalize(dbparameters->q_prevnsec3_);
dbparameters->q_prevnsec3_ = NULL;
sqlite3_finalize(dbparameters->q_nsec3_);
dbparameters->q_nsec3_ = NULL;
*/
for
(
int
i
=
0
;
i
<
NUM_STATEMENTS
;
++
i
)
{
sqlite3_finalize
(
dbparameters_
->
statements_
[
i
]);
dbparameters_
->
statements_
[
i
]
=
NULL
;
}
sqlite3_close
(
dbparameters_
->
db_
);
dbparameters_
->
db_
=
NULL
;
...
...
@@ -288,36 +264,38 @@ SQLite3Database::close(void) {
std
::
pair
<
bool
,
int
>
SQLite3Database
::
getZone
(
const
isc
::
dns
::
Name
&
name
)
const
{
return
(
getZone
(
name
.
toText
()));
}
std
::
pair
<
bool
,
int
>
SQLite3Database
::
getZone
(
const
string
&
name
)
const
{
int
rc
;
sqlite3_stmt
*
const
stmt
=
dbparameters_
->
statements_
[
ZONE
];
// Take the statement (simple SELECT id FROM zones WHERE...)
// and prepare it (bind the parameters to it)
sqlite3_reset
(
dbparameters_
->
q_zone_
);
rc
=
sqlite3_bind_text
(
dbparameters_
->
q_zone_
,
1
,
name
.
toText
().
c_str
(),
-
1
,
SQLITE_TRANSIENT
);
sqlite3_reset
(
stmt
);
rc
=
sqlite3_bind_text
(
stmt
,
1
,
name
.
c_str
(),
-
1
,
SQLITE_STATIC
);
if
(
rc
!=
SQLITE_OK
)
{
isc_throw
(
SQLite3Error
,
"Could not bind "
<<
name
<<
" to SQL statement (zone)"
);
}
rc
=
sqlite3_bind_text
(
dbparameters_
->
q_zone_
,
2
,
class_
.
c_str
(),
-
1
,
SQLITE_STATIC
);
rc
=
sqlite3_bind_text
(
stmt
,
2
,
class_
.
c_str
(),
-
1
,
SQLITE_STATIC
);
if
(
rc
!=
SQLITE_OK
)
{
isc_throw
(
SQLite3Error
,
"Could not bind "
<<
class_
<<
" to SQL statement (zone)"
);
}
// Get the data there and see if it found anything
rc
=
sqlite3_step
(
dbparameters_
->
q_zone_
);
rc
=
sqlite3_step
(
stmt
);
std
::
pair
<
bool
,
int
>
result
;
if
(
rc
==
SQLITE_ROW
)
{
result
=
std
::
pair
<
bool
,
int
>
(
true
,
sqlite3_column_int
(
dbparameters_
->
q_zone_
,
0
));
result
=
std
::
pair
<
bool
,
int
>
(
true
,
sqlite3_column_int
(
stmt
,
0
));
}
else
{
result
=
std
::
pair
<
bool
,
int
>
(
false
,
0
);
}
// Free resources
sqlite3_reset
(
dbparameters_
->
q_zone_
);
sqlite3_reset
(
stmt
);
return
(
result
);
}
...
...
@@ -325,14 +303,16 @@ SQLite3Database::getZone(const isc::dns::Name& name) const {
void
SQLite3Database
::
searchForRecords
(
int
zone_id
,
const
std
::
string
&
name
)
{
resetSearch
();
if
(
sqlite3_bind_int
(
dbparameters_
->
q_any_
,
1
,
zone_id
)
!=
SQLITE_OK
)
{
sqlite3_stmt
*
const
stmt
=
dbparameters_
->
statements_
[
ANY
];
if
(
sqlite3_bind_int
(
stmt
,
1
,
zone_id
)
!=
SQLITE_OK
)
{
isc_throw
(
DataSourceError
,
"Error in sqlite3_bind_int() for zone_id "
<<
zone_id
<<
": "
<<
sqlite3_errmsg
(
dbparameters_
->
db_
));
}
// use transient since name is a ref and may disappear
if
(
sqlite3_bind_text
(
dbparameters_
->
q_any_
,
2
,
name
.
c_str
(),
-
1
,
SQLITE_TRANSIENT
)
!=
SQLITE_OK
)
{
if
(
sqlite3_bind_text
(
stmt
,
2
,
name
.
c_str
(),
-
1
,
SQLITE_TRANSIENT
)
!=
SQLITE_OK
)
{
isc_throw
(
DataSourceError
,
"Error in sqlite3_bind_text() for name "
<<
name
<<
": "
<<
sqlite3_errmsg
(
dbparameters_
->
db_
));
...
...
@@ -376,7 +356,7 @@ SQLite3Database::getNextRecord(std::string columns[], size_t column_count) {
"of size "
<<
COLUMN_COUNT
<<
" to getNextRecord()"
);
}
sqlite3_stmt
*
current_stmt
=
dbparameters_
->
q_any_
;
sqlite3_stmt
*
current_stmt
=
dbparameters_
->
statements_
[
ANY
]
;
const
int
rc
=
sqlite3_step
(
current_stmt
);
if
(
rc
==
SQLITE_ROW
)
{
...
...
@@ -404,8 +384,127 @@ SQLite3Database::getNextRecord(std::string columns[], size_t column_count) {
void
SQLite3Database
::
resetSearch
()
{
sqlite3_reset
(
dbparameters_
->
q_any_
);
sqlite3_clear_bindings
(
dbparameters_
->
q_any_
);
sqlite3_reset
(
dbparameters_
->
statements_
[
ANY
]);
sqlite3_clear_bindings
(
dbparameters_
->
statements_
[
ANY
]);
}
pair
<
bool
,
int
>
SQLite3Database
::
startUpdateZone
(
const
string
&
zone_name
,
const
bool
replace
)
{
if
(
dbparameters_
->
updating_zone
)
{
isc_throw
(
DataSourceError
,
"duplicate zone update on SQLite3 data source"
);
}
const
pair
<
bool
,
int
>
zone_info
(
getZone
(
zone_name
));
if
(
!
zone_info
.
first
)
{
return
(
zone_info
);
}
dbparameters_
->
updating_zone
=
true
;
dbparameters_
->
updated_zone_id
=
zone_info
.
second
;
StatementExecuter
(
dbparameters_
,
BEGIN
,
"start an SQLite3 transaction"
).
exec
();
if
(
replace
)
{
StatementExecuter
delzone_exec
(
dbparameters_
,
DEL_ZONE_RECORDS
,
"delete zone records"
);
sqlite3_clear_bindings
(
dbparameters_
->
statements_
[
DEL_ZONE_RECORDS
]);
if
(
sqlite3_bind_int
(
dbparameters_
->
statements_
[
DEL_ZONE_RECORDS
],
1
,
zone_info
.
second
)
!=
SQLITE_OK
)
{
isc_throw
(
DataSourceError
,
"failed to bind SQLite3 parameter: "
<<
sqlite3_errmsg
(
dbparameters_
->
db_
));
}
delzone_exec
.
exec
();
}
return
(
zone_info
);
}
void
SQLite3Database
::
commitUpdateZone
()
{
if
(
!
dbparameters_
->
updating_zone
)
{
isc_throw
(
DataSourceError
,
"committing zone update on SQLite3 "
"data source without transaction"
);
}
StatementExecuter
(
dbparameters_
,
COMMIT
,
"commit an SQLite3 transaction"
).
exec
();
dbparameters_
->
updating_zone
=
false
;
dbparameters_
->
updated_zone_id
=
-
1
;
}
void
SQLite3Database
::
rollbackUpdateZone
()
{
if
(
!
dbparameters_
->
updating_zone
)
{
isc_throw
(
DataSourceError
,
"rolling back zone update on SQLite3 "
"data source without transaction"
);
}
// We expect that ROLLBACK always succeeds. But could that fail?
// If so, what should we do? Is it okay to simply propagate the
// DataSourceError exception?
StatementExecuter
(
dbparameters_
,
ROLLBACK
,
"commit an SQLite3 transaction"
).
exec
();
dbparameters_
->
updating_zone
=
false
;
dbparameters_
->
updated_zone_id
=
-
1
;
}
namespace
{
// Commonly used code sequence for adding/deleting record
void
doUpdate
(
SQLite3Parameters
*
dbparams
,
StatementID
stmt_id
,
const
vector
<
string
>&
update_params
,
const
char
*
exec_desc
)
{
sqlite3_stmt
*
const
stmt
=
dbparams
->
statements_
[
stmt_id
];
StatementExecuter
executer
(
dbparams
,
stmt_id
,
exec_desc
);
int
param_id
=
0
;
if
(
sqlite3_bind_int
(
stmt
,
++
param_id
,
dbparams
->
updated_zone_id
)
!=
SQLITE_OK
)
{
isc_throw
(
DataSourceError
,
"failed to bind SQLite3 parameter: "
<<
sqlite3_errmsg
(
dbparams
->
db_
));
}
BOOST_FOREACH
(
const
string
&
column
,
update_params
)
{
if
(
sqlite3_bind_text
(
stmt
,
++
param_id
,
column
.
c_str
(),
-
1
,
SQLITE_TRANSIENT
)
!=
SQLITE_OK
)
{
isc_throw
(
DataSourceError
,
"failed to bind SQLite3 parameter: "
<<
sqlite3_errmsg
(
dbparams
->
db_
));
}
}
executer
.
exec
();
}
}
void
SQLite3Database
::
addRecordToZone
(
const
vector
<
string
>&
columns
)
{
if
(
!
dbparameters_
->
updating_zone
)
{
isc_throw
(
DataSourceError
,
"adding record to SQLite3 "
"data source without transaction"
);
}
if
(
columns
.
size
()
!=
ADD_COLUMN_COUNT
)
{
isc_throw
(
DataSourceError
,
"adding incompatible number of columns "
"to SQLite3 data source: "
<<
columns
.
size
());
}
doUpdate
(
dbparameters_
,
ADD_RECORD
,
columns
,
"add record to zone"
);
}
void
SQLite3Database
::
deleteRecordInZone
(
const
vector
<
string
>&
params
)
{
if
(
!
dbparameters_
->
updating_zone
)
{
isc_throw
(
DataSourceError
,
"deleting record in SQLite3 "
"data source without transaction"
);
}
if
(
params
.
size
()
!=
DEL_PARAM_COUNT
)
{
isc_throw
(
DataSourceError
,
"incompatible # of parameters for "
"deleting in SQLite3 data source: "
<<
params
.
size
());
}
doUpdate
(
dbparameters_
,
DEL_RECORD
,
params
,
"delete record from zone"
);
}
}
...
...
src/lib/datasrc/sqlite3_accessor.h
View file @
1351cb42
...
...
@@ -135,6 +135,30 @@ public:
*/
virtual
void
resetSearch
();
/// TBD
/// This cannot be nested.
virtual
std
::
pair
<
bool
,
int
>
startUpdateZone
(
const
std
::
string
&
zone_name
,
bool
replace
);
/// TBD
/// Note: we are quite impatient here: it's quite possible that the COMMIT
/// fails due to other process performing SELECT on the same database
/// (consider the case where COMMIT is done by xfrin or dynamic update
/// server while an authoritative server is busy reading the DB).
/// In a future version we should probably need to introduce some retry
/// attempt and/or increase timeout before giving up the COMMIT, even
/// if it still doesn't guarantee 100% success.
virtual
void
commitUpdateZone
();
/// TBD
virtual
void
rollbackUpdateZone
();
/// TBD
virtual
void
addRecordToZone
(
const
std
::
vector
<
std
::
string
>&
columns
);
/// TBD
virtual
void
deleteRecordInZone
(
const
std
::
vector
<
std
::
string
>&
params
);
/// The SQLite3 implementation of this method returns a string starting
/// with a fixed prefix of "sqlite3_" followed by the DB file name
/// removing any path name. For example, for the DB file
...
...
@@ -143,6 +167,11 @@ public:
virtual
const
std
::
string
&
getDBName
()
const
{
return
(
database_name_
);
}
private:
// same as the public version except it takes name as a string
// (actually this is the intended interface. this should replace the
// current public version).
std
::
pair
<
bool
,
int
>
getZone
(
const
std
::
string
&
name
)
const
;
/// \brief Private database data
SQLite3Parameters
*
dbparameters_
;
/// \brief The class for which the queries are done
...
...
@@ -157,4 +186,8 @@ private:
}
}
#endif
#endif // __DATASRC_SQLITE3_CONNECTION_H
// Local Variables:
// mode: c++
// End:
src/lib/datasrc/tests/Makefile.am
View file @
1351cb42
SUBDIRS
=
.
testdata
AM_CPPFLAGS
=
-I
$(top_srcdir)
/src/lib
-I
$(top_builddir)
/src/lib
AM_CPPFLAGS
+=
-I
$(top_builddir)
/src/lib/dns
-I
$(top_srcdir)
/src/lib/dns
AM_CPPFLAGS
+=
$(BOOST_INCLUDES)
AM_CPPFLAGS
+=
$(SQLITE_CFLAGS)
AM_CPPFLAGS
+=
-DTEST_DATA_DIR
=
\"
$(srcdir)
/testdata
\"
AM_CPPFLAGS
+=
-DTEST_DATA_DIR
=
\"
$(abs_srcdir)
/testdata
\"
AM_CPPFLAGS
+=
-DTEST_DATA_BUILDDIR
=
\"
$(abs_builddir)
/testdata
\"
AM_CPPFLAGS
+=
-DINSTALL_PROG
=
\"
$(abs_top_srcdir)
/install-sh
\"
AM_CXXFLAGS
=
$(B10_CXXFLAGS)
...
...
src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
View file @
1351cb42
...
...
@@ -11,6 +11,9 @@
// 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 <vector>
#include <datasrc/sqlite3_accessor.h>
#include <datasrc/data_source.h>
...
...
@@ -20,7 +23,9 @@
#include <gtest/gtest.h>
#include <boost/scoped_ptr.hpp>
using
namespace
std
;
using
namespace
isc
::
datasrc
;
using
boost
::
shared_ptr
;
using
isc
::
data
::
ConstElementPtr
;
using
isc
::
data
::
Element
;
using
isc
::
dns
::
RRClass
;
...
...
@@ -242,4 +247,287 @@ TEST_F(SQLite3Access, getRecords) {
"33495 example.com. FAKEFAKEFAKEFAKE"
);
}
//
// Commonly used data for update tests
//
const
char
*
const
common_expected_data
[]
=
{
// Test record already stored in the tested sqlite3 DB file.
"foo.bar.example.com."
,
"com.example.bar.foo."
,
"3600"
,
"A"
,
""
,
"192.0.2.1"
};
const
char
*
const
new_data
[]
=
{
// Newly added data commonly used by some of the tests below
"newdata.example.com."
,
"com.example.newdata."
,
"3600"
,
"A"
,
""
,
"192.0.2.1"
};
const
char
*
const
deleted_data
[]
=
{
// Existing data to be removed commonly used by some of the tests below
"foo.bar.example.com."
,
"A"
,
"192.0.2.1"
};
class
SQLite3Update
:
public
SQLite3Access
{
protected:
SQLite3Update
()
{
ASSERT_EQ
(
0
,
system
(
INSTALL_PROG
" "
TEST_DATA_DIR
"/test.sqlite3 "
TEST_DATA_BUILDDIR
"/test.sqlite3.copied"
));
initAccessor
(
TEST_DATA_BUILDDIR
"/test.sqlite3.copied"
,
RRClass
::
IN
());
zone_id
=
db
->
getZone
(
Name
(
"example.com"
)).
second
;
another_db
.
reset
(
new
SQLite3Database
(
TEST_DATA_BUILDDIR
"/test.sqlite3.copied"
,
RRClass
::
IN
()));
expected_stored
.
push_back
(
common_expected_data
);
}
int
zone_id
;
std
::
string
get_columns
[
DatabaseAccessor
::
COLUMN_COUNT
];
std
::
vector
<
std
::
string
>
update_columns
;
vector
<
const
char
*
const
*>
expected_stored
;
// placeholder for checkRecords
vector
<
const
char
*
const
*>
empty_stored
;
// indicate no corresponding data
// Another accessor, emulating one running on a different process/thread
shared_ptr
<
SQLite3Database
>
another_db
;
};
void
checkRecords
(
SQLite3Database
&
db
,
int
zone_id
,
const
std
::
string
&
name
,
vector
<
const
char
*
const
*>
expected_rows
)
{
db
.
searchForRecords
(
zone_id
,
name
);
std
::
string
columns
[
DatabaseAccessor
::
COLUMN_COUNT
];
vector
<
const
char
*
const
*>::
const_iterator
it
=
expected_rows
.
begin
();
while
(
db
.
getNextRecord
(
columns
,
DatabaseAccessor
::
COLUMN_COUNT
))
{
ASSERT_TRUE
(
it
!=
expected_rows
.
end
());
checkRecordRow
(
columns
,
(
*
it
)[
3
],
(
*
it
)[
2
],
(
*
it
)[
4
],
(
*
it
)[
5
]);
++
it
;
}
EXPECT_TRUE
(
it
==
expected_rows
.
end
());