Kea 2.7.6
versioned_csv_file.cc
Go to the documentation of this file.
1// Copyright (C) 2015-2021 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
10
11namespace isc {
12namespace util {
13
14VersionedCSVFile::VersionedCSVFile(const std::string& filename)
15 : CSVFile(filename), columns_(0), valid_column_count_(0),
16 minimum_valid_columns_(0), input_header_count_(0),
17 input_schema_state_(CURRENT) {
18}
19
22
23void
24VersionedCSVFile::addColumn(const std::string& name,
25 const std::string& version,
26 const std::string& default_value) {
28 columns_.push_back(VersionedColumnPtr(new VersionedColumn(name, version,
29 default_value)));
30}
31
32void
33VersionedCSVFile::setMinimumValidColumns(const std::string& column_name) {
34 try {
35 int index = getColumnIndex(column_name);
36 minimum_valid_columns_ = index + 1;
37
38 } catch (...) {
40 "setMinimumValidColumns: " << column_name << " is not "
41 "defined");
42 }
43}
44
45size_t
47 return (minimum_valid_columns_);
48}
49
50size_t
52 return (valid_column_count_);
53}
54
55size_t
57 return (input_header_count_);
58}
59
60void
61VersionedCSVFile::open(const bool seek_to_end) {
62 if (getColumnCount() == 0) {
64 "no schema has been defined, cannot open CSV file :"
65 << getFilename());
66 }
67
68 CSVFile::open(seek_to_end);
69}
70
71void
73 if (getColumnCount() == 0) {
75 "no schema has been defined, cannot create CSV file :"
76 << getFilename());
77 }
78
80 // For new files they always match.
81 input_header_count_ = valid_column_count_ = getColumnCount();
82}
83
86 return (input_schema_state_);
87}
88
89bool
91 return (input_schema_state_ != CURRENT);
92}
93
94std::string
96 if (getValidColumnCount() > 0) {
97 return (getVersionedColumn(getValidColumnCount() - 1)->version_);
98 }
99
100 return ("undefined");
101}
102
103std::string
105 if (getColumnCount() > 0) {
106 return (getVersionedColumn(getColumnCount() - 1)->version_);
107 }
108
109 return ("undefined");
110}
111
113VersionedCSVFile::getVersionedColumn(const size_t index) const {
114 if (index >= getColumnCount()) {
115 isc_throw(isc::OutOfRange, "versioned column index " << index
116 << " out of range; CSV file : " << getFilename()
117 << " only has " << getColumnCount() << " columns ");
118 }
119
120 return (columns_[index]);
121}
122
123bool
125 setReadMsg("success");
126 // Use base class to physical read the row, but skip its row
127 // validation
128 CSVFile::next(row, true);
129 if (row == CSVFile::EMPTY_ROW()) {
130 return(true);
131 }
132
133 bool row_valid = true;
134 switch(getInputSchemaState()) {
135 case CURRENT:
136 // All rows must match than the current schema
137 if (row.getValuesCount() != getColumnCount()) {
138 columnCountError(row, "must match current schema");
139 row_valid = false;
140 }
141 break;
142
143 case NEEDS_UPGRADE:
144 // The input header met the minimum column count but
145 // is less than the current schema so:
146 // Rows must not be shorter than the valid column count
147 // and not longer than the current schema
148 if (row.getValuesCount() < getValidColumnCount()) {
149 columnCountError(row, "too few columns to upgrade");
150 row_valid = false;
151 } else if (row.getValuesCount() > getColumnCount()) {
152 columnCountError(row, "too many columns to upgrade");
153 row_valid = false;
154 } else {
155 // Add any missing values
156 for (size_t index = row.getValuesCount();
157 index < getColumnCount(); ++index) {
158 row.append(columns_[index]->default_value_);
159 }
160 }
161 break;
162
163 case NEEDS_DOWNGRADE:
164 // The input header exceeded current schema so:
165 // Rows may be as long as input header but not shorter than
166 // the current schema
167 if (row.getValuesCount() < getColumnCount()) {
168 columnCountError(row, "too few columns to downgrade");
169 } else if (row.getValuesCount() > getInputHeaderCount()) {
170 columnCountError(row, "too many columns to downgrade");
171 } else {
172 // Toss any the extra columns
173 row.trim(row.getValuesCount() - getColumnCount());
174 }
175 break;
176 }
177
178 return (row_valid);
179}
180
181void
183 const std::string& reason) {
184 std::ostringstream s;
185 s << "Invalid number of columns: "
186 << row.getValuesCount() << " in row: '" << row
187 << "', file: '" << getFilename() << "' : " << reason;
188 setReadMsg(s.str());
189}
190
191bool
193 if (getColumnCount() == 0) {
195 "cannot validate header, no schema has been defined");
196 }
197
198 input_header_count_ = header.getValuesCount();
199
200 // Iterate over the number of columns in the header, testing
201 // each against the defined column in the same position.
202 // If there is a mismatch, bail.
203 size_t i = 0;
204 for ( ; i < getInputHeaderCount() && i < getColumnCount(); ++i) {
205 if (getColumnName(i) != header.readAt(i)) {
206 std::ostringstream s;
207 s << " - header contains an invalid column: '"
208 << header.readAt(i) << "'";
209 setReadMsg(s.str());
210 return (false);
211 }
212 }
213
214 // If we found too few valid columns, then we cannot convert this
215 // file. It's too old, too corrupt, or not a Kea file.
216 if (i < getMinimumValidColumns()) {
217 std::ostringstream s;
218 s << " - header has only " << i << " valid column(s), "
219 << "it must have at least " << getMinimumValidColumns();
220 setReadMsg(s.str());
221 return (false);
222 }
223
224 // Remember the number of valid columns we found. When this number
225 // is less than the number of defined columns, then we have an older
226 // version of the lease file. We'll need this value to validate
227 // and upgrade data rows.
228 valid_column_count_ = i;
229
231 input_schema_state_ = NEEDS_UPGRADE;
232 } else if (getInputHeaderCount() > getColumnCount()) {
233 // If there are more values in the header than defined columns
234 // then, we'll drop the extra. This allows someone to attempt to
235 // downgrade if need be.
236 input_schema_state_ = NEEDS_DOWNGRADE;
237 std::ostringstream s;
238 s << " - header has " << getInputHeaderCount() - getColumnCount()
239 << " extra column(s), these will be ignored";
240 setReadMsg(s.str());
241 }
242
243 return (true);
244}
245
246} // end of isc::util namespace
247} // end of isc namespace
int version()
returns Kea hooks version.
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
Provides input/output access to CSV files.
Definition csv_file.h:358
std::string getColumnName(const size_t col_index) const
Returns the name of the column.
Definition csv_file.cc:247
size_t getColumnCount() const
Returns the number of columns in the file.
Definition csv_file.h:403
static CSVRow EMPTY_ROW()
Represents empty row.
Definition csv_file.h:491
void setReadMsg(const std::string &read_msg)
Sets error message after row validation.
Definition csv_file.h:486
std::string getFilename() const
Returns the path to the CSV file.
Definition csv_file.h:408
virtual void recreate()
Creates a new CSV file.
Definition csv_file.cc:370
void addColumn(const std::string &col_name)
Adds new column name.
Definition csv_file.cc:147
size_t getColumnIndex(const std::string &col_name) const
Returns the index of the column having specified name.
Definition csv_file.cc:237
virtual void open(const bool seek_to_end=false)
Opens existing file or creates a new one.
Definition csv_file.cc:302
bool next(CSVRow &row, const bool skip_validation=false)
Reads next row from CSV file.
Definition csv_file.cc:257
Represents a single row of the CSV file.
Definition csv_file.h:51
size_t getValuesCount() const
Returns number of values in a CSV row.
Definition csv_file.h:85
void trim(const size_t count)
Trims a given number of elements from the end of a row.
Definition csv_file.cc:95
std::string readAt(const size_t at) const
Retrieves a value from the internal container.
Definition csv_file.cc:60
void append(const T value)
Appends the value as a new column.
Definition csv_file.h:220
Exception thrown when an error occurs during CSV file processing.
bool needsConversion() const
Returns true if the input file schema state is not CURRENT.
virtual void open(const bool seek_to_end=false)
Opens existing file or creates a new one.
virtual ~VersionedCSVFile()
Destructor.
const VersionedColumnPtr & getVersionedColumn(const size_t index) const
Fetch the column descriptor for a given index.
std::string getSchemaVersion() const
text version of current schema supported by the file's metadata
InputSchemaState
Possible input file schema states.
size_t getInputHeaderCount() const
Returns the number of columns found in the input header.
virtual bool validateHeader(const CSVRow &header)
Validates the header of a VersionedCSVFile.
VersionedCSVFile(const std::string &filename)
Constructor.
std::string getInputSchemaVersion() const
Returns the schema version of the physical file.
virtual void recreate()
Creates a new CSV file.
void setMinimumValidColumns(const std::string &column_name)
Sets the minimum number of valid columns based on a given column.
bool next(CSVRow &row)
Reads next row from the file file.
void addColumn(const std::string &col_name, const std::string &version, const std::string &default_value="")
Adds metadata for a single column to the schema.
size_t getMinimumValidColumns() const
Returns the minimum number of columns which must be present for the file to be considered valid.
void columnCountError(const CSVRow &row, const std::string &reason)
Convenience method for adding an error message.
size_t getValidColumnCount() const
Returns the number of valid columns found in the header For newly created files this will always matc...
enum InputSchemaState getInputSchemaState() const
Fetches the state of the input file's schema.
Contains the metadata for a single column in a file.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< VersionedColumn > VersionedColumnPtr
Defines a smart pointer to VersionedColumn.
Defines the logger used by the top-level component of kea-lfc.