123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- // Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this
- // file, You can obtain one at http://mozilla.org/MPL/2.0/.
- #include <util/versioned_csv_file.h>
- namespace isc {
- namespace util {
- VersionedCSVFile::VersionedCSVFile(const std::string& filename)
- : CSVFile(filename), columns_(0), valid_column_count_(0),
- minimum_valid_columns_(0), input_header_count_(0),
- input_schema_state_(CURRENT) {
- }
- VersionedCSVFile::~VersionedCSVFile() {
- }
- void
- VersionedCSVFile::addColumn(const std::string& name,
- const std::string& version,
- const std::string& default_value) {
- CSVFile::addColumn(name);
- columns_.push_back(VersionedColumnPtr(new VersionedColumn(name, version,
- default_value)));
- }
- void
- VersionedCSVFile::setMinimumValidColumns(const std::string& column_name) {
- try {
- int index = getColumnIndex(column_name);
- minimum_valid_columns_ = index + 1;
- } catch (...) {
- isc_throw(VersionedCSVFileError,
- "setMinimumValidColumns: " << column_name << " is not "
- "defined");
- }
- }
- size_t
- VersionedCSVFile::getMinimumValidColumns() const {
- return (minimum_valid_columns_);
- }
- size_t
- VersionedCSVFile::getValidColumnCount() const {
- return (valid_column_count_);
- }
- size_t
- VersionedCSVFile::getInputHeaderCount() const {
- return (input_header_count_);
- }
- void
- VersionedCSVFile::open(const bool seek_to_end) {
- if (getColumnCount() == 0) {
- isc_throw(VersionedCSVFileError,
- "no schema has been defined, cannot open CSV file :"
- << getFilename());
- }
- CSVFile::open(seek_to_end);
- }
- void
- VersionedCSVFile::recreate() {
- if (getColumnCount() == 0) {
- isc_throw(VersionedCSVFileError,
- "no schema has been defined, cannot create CSV file :"
- << getFilename());
- }
- CSVFile::recreate();
- // For new files they always match.
- input_header_count_ = valid_column_count_ = getColumnCount();
- }
- VersionedCSVFile::InputSchemaState
- VersionedCSVFile::getInputSchemaState() const {
- return (input_schema_state_);
- }
- bool
- VersionedCSVFile::needsConversion() const {
- return (input_schema_state_ != CURRENT);
- }
- std::string
- VersionedCSVFile::getInputSchemaVersion() const {
- if (getValidColumnCount() > 0) {
- return (getVersionedColumn(getValidColumnCount() - 1)->version_);
- }
- return ("undefined");
- }
- std::string
- VersionedCSVFile::getSchemaVersion() const {
- if (getColumnCount() > 0) {
- return (getVersionedColumn(getColumnCount() - 1)->version_);
- }
- return ("undefined");
- }
- const VersionedColumnPtr&
- VersionedCSVFile::getVersionedColumn(const size_t index) const {
- if (index >= getColumnCount()) {
- isc_throw(isc::OutOfRange, "versioned column index " << index
- << " out of range; CSV file : " << getFilename()
- << " only has " << getColumnCount() << " columns ");
- }
- return (columns_[index]);
- }
- bool
- VersionedCSVFile::next(CSVRow& row) {
- setReadMsg("success");
- // Use base class to physical read the row, but skip its row
- // validation
- CSVFile::next(row, true);
- if (row == CSVFile::EMPTY_ROW()) {
- return(true);
- }
- bool row_valid = true;
- switch(getInputSchemaState()) {
- case CURRENT:
- // All rows must match than the current schema
- if (row.getValuesCount() != getColumnCount()) {
- columnCountError(row, "must match current schema");
- row_valid = false;
- }
- break;
- case NEEDS_UPGRADE:
- // The input header met the minimum column count but
- // is less than the current schema so:
- // Rows must not be shorter than the valid column count
- // and not longer than the current schema
- if (row.getValuesCount() < getValidColumnCount()) {
- columnCountError(row, "too few columns to upgrade");
- row_valid = false;
- } else if (row.getValuesCount() > getColumnCount()) {
- columnCountError(row, "too many columns to upgrade");
- row_valid = false;
- } else {
- // Add any missing values
- for (size_t index = row.getValuesCount();
- index < getColumnCount(); ++index) {
- row.append(columns_[index]->default_value_);
- }
- }
- break;
- case NEEDS_DOWNGRADE:
- // The input header exceeded current schema so:
- // Rows may be as long as input header but not shorter than
- // the the current schema
- if (row.getValuesCount() < getColumnCount()) {
- columnCountError(row, "too few columns to downgrade");
- } else if (row.getValuesCount() > getInputHeaderCount()) {
- columnCountError(row, "too many columns to downgrade");
- } else {
- // Toss any the extra columns
- row.trim(row.getValuesCount() - getColumnCount());
- }
- break;
- }
- return (row_valid);
- }
- void
- VersionedCSVFile::columnCountError(const CSVRow& row,
- const std::string& reason) {
- std::ostringstream s;
- s << "Invalid number of columns: "
- << row.getValuesCount() << " in row: '" << row
- << "', file: '" << getFilename() << "' : " << reason;
- setReadMsg(s.str());
- }
- bool
- VersionedCSVFile::validateHeader(const CSVRow& header) {
- if (getColumnCount() == 0) {
- isc_throw(VersionedCSVFileError,
- "cannot validate header, no schema has been defined");
- }
- input_header_count_ = header.getValuesCount();
- // Iterate over the number of columns in the header, testing
- // each against the defined column in the same position.
- // If there is a mismatch, bail.
- size_t i = 0;
- for ( ; i < getInputHeaderCount() && i < getColumnCount(); ++i) {
- if (getColumnName(i) != header.readAt(i)) {
- std::ostringstream s;
- s << " - header contains an invalid column: '"
- << header.readAt(i) << "'";
- setReadMsg(s.str());
- return (false);
- }
- }
- // If we found too few valid columns, then we cannot convert this
- // file. It's too old, too corrupt, or not a Kea file.
- if (i < getMinimumValidColumns()) {
- std::ostringstream s;
- s << " - header has only " << i << " valid column(s), "
- << "it must have at least " << getMinimumValidColumns();
- setReadMsg(s.str());
- return (false);
- }
- // Remember the number of valid columns we found. When this number
- // is less than the number of defined columns, then we have an older
- // version of the lease file. We'll need this value to validate
- // and upgrade data rows.
- valid_column_count_ = i;
- if (getValidColumnCount() < getColumnCount()) {
- input_schema_state_ = NEEDS_UPGRADE;
- } else if (getInputHeaderCount() > getColumnCount()) {
- // If there are more values in the header than defined columns
- // then, we'll drop the extra. This allows someone to attempt to
- // downgrade if need be.
- input_schema_state_ = NEEDS_DOWNGRADE;
- std::ostringstream s;
- s << " - header has " << getInputHeaderCount() - getColumnCount()
- << " extra column(s), these will be ignored";
- setReadMsg(s.str());
- }
- return (true);
- }
- } // end of isc::util namespace
- } // end of isc namespace
|