Kea 3.3.1
versioned_csv_file.cc
Go to the documentation of this file.
1// Copyright (C) 2015-2026 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 physically read the row, but skip its row
127 // validation. Propagate I/O and stream failures: on error the
128 // base class leaves 'row' unchanged, so continuing would validate
129 // stale contents and incorrectly report success.
130 if (!CSVFile::next(row, true)) {
131 return (false);
132 }
133 if (row == CSVFile::EMPTY_ROW()) {
134 return (true);
135 }
136
137 bool row_valid = true;
138 switch(getInputSchemaState()) {
139 case CURRENT:
140 // All rows must match than the current schema
141 if (row.getValuesCount() != getColumnCount()) {
142 columnCountError(row, "must match current schema");
143 row_valid = false;
144 }
145 break;
146
147 case NEEDS_UPGRADE:
148 // The input header met the minimum column count but
149 // is less than the current schema so:
150 // Rows must not be shorter than the valid column count
151 // and not longer than the current schema
152 if (row.getValuesCount() < getValidColumnCount()) {
153 columnCountError(row, "too few columns to upgrade");
154 row_valid = false;
155 } else if (row.getValuesCount() > getColumnCount()) {
156 columnCountError(row, "too many columns to upgrade");
157 row_valid = false;
158 } else {
159 // Add any missing values
160 for (size_t index = row.getValuesCount();
161 index < getColumnCount(); ++index) {
162 row.append(columns_[index]->default_value_);
163 }
164 }
165 break;
166
167 case NEEDS_DOWNGRADE:
168 // The input header exceeded current schema so:
169 // Rows may be as long as input header but not shorter than
170 // the current schema
171 if (row.getValuesCount() < getColumnCount()) {
172 columnCountError(row, "too few columns to downgrade");
173 row_valid = false;
174 } else if (row.getValuesCount() > getInputHeaderCount()) {
175 columnCountError(row, "too many columns to downgrade");
176 row_valid = false;
177 } else {
178 // Toss any the extra columns
179 row.trim(row.getValuesCount() - getColumnCount());
180 }
181 break;
182 }
183
184 return (row_valid);
185}
186
187void
189 const std::string& reason) {
190 std::ostringstream s;
191 s << "Invalid number of columns: "
192 << row.getValuesCount() << " in row: '" << row
193 << "', file: '" << getFilename() << "' : " << reason;
194 setReadMsg(s.str());
195}
196
197bool
199 if (getColumnCount() == 0) {
201 "cannot validate header, no schema has been defined");
202 }
203
204 input_header_count_ = header.getValuesCount();
205
206 // Iterate over the number of columns in the header, testing
207 // each against the defined column in the same position.
208 // If there is a mismatch, bail.
209 size_t i = 0;
210 for ( ; i < getInputHeaderCount() && i < getColumnCount(); ++i) {
211 if (getColumnName(i) != header.readAt(i)) {
212 std::ostringstream s;
213 s << " - header contains an invalid column: '"
214 << header.readAt(i) << "'";
215 setReadMsg(s.str());
216 return (false);
217 }
218 }
219
220 // If we found too few valid columns, then we cannot convert this
221 // file. It's too old, too corrupt, or not a Kea file.
222 if (i < getMinimumValidColumns()) {
223 std::ostringstream s;
224 s << " - header has only " << i << " valid column(s), "
225 << "it must have at least " << getMinimumValidColumns();
226 setReadMsg(s.str());
227 return (false);
228 }
229
230 // Remember the number of valid columns we found. When this number
231 // is less than the number of defined columns, then we have an older
232 // version of the lease file. We'll need this value to validate
233 // and upgrade data rows.
234 valid_column_count_ = i;
235
237 input_schema_state_ = NEEDS_UPGRADE;
238 } else if (getInputHeaderCount() > getColumnCount()) {
239 // If there are more values in the header than defined columns
240 // then, we'll drop the extra. This allows someone to attempt to
241 // downgrade if need be.
242 input_schema_state_ = NEEDS_DOWNGRADE;
243 std::ostringstream s;
244 s << " - header has " << getInputHeaderCount() - getColumnCount()
245 << " extra column(s), these will be ignored";
246 setReadMsg(s.str());
247 }
248
249 return (true);
250}
251
252} // end of isc::util namespace
253} // end of isc namespace
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
std::string getColumnName(const size_t col_index) const
Returns the name of the column.
Definition csv_file.cc:267
size_t getColumnCount() const
Returns the number of columns in the file.
Definition csv_file.h:419
static CSVRow EMPTY_ROW()
Represents empty row.
Definition csv_file.h:507
void setReadMsg(const std::string &read_msg)
Sets error message after row validation.
Definition csv_file.h:502
CSVFile(const std::string &filename)
Constructor.
Definition csv_file.cc:115
std::string getFilename() const
Returns the path to the CSV file.
Definition csv_file.h:424
virtual void recreate()
Creates a new CSV file.
Definition csv_file.cc:390
void addColumn(const std::string &col_name)
Adds new column name.
Definition csv_file.cc:153
size_t getColumnIndex(const std::string &col_name) const
Returns the index of the column having specified name.
Definition csv_file.cc:257
virtual void open(const bool seek_to_end=false)
Opens existing file or creates a new one.
Definition csv_file.cc:322
bool next(CSVRow &row, const bool skip_validation=false)
Reads next row from CSV file.
Definition csv_file.cc:277
Represents a single row of the CSV file.
Definition csv_file.h:59
size_t getValuesCount() const
Returns number of values in a CSV row.
Definition csv_file.h:93
void trim(const size_t count)
Trims a given number of elements from the end of a row.
Definition csv_file.cc:96
std::string readAt(const size_t at) const
Retrieves a value from the internal container.
Definition csv_file.cc:61
void append(const T value)
Appends the value as a new column.
Definition csv_file.h:228
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.
int version()
returns Kea hooks version.
#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.