Kea  2.1.7-git
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 
11 namespace isc {
12 namespace util {
13 
14 VersionedCSVFile::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 
21 }
22 
23 void
24 VersionedCSVFile::addColumn(const std::string& name,
25  const std::string& version,
26  const std::string& default_value) {
27  CSVFile::addColumn(name);
28  columns_.push_back(VersionedColumnPtr(new VersionedColumn(name, version,
29  default_value)));
30 }
31 
32 void
33 VersionedCSVFile::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 
45 size_t
47  return (minimum_valid_columns_);
48 }
49 
50 size_t
52  return (valid_column_count_);
53 }
54 
55 size_t
57  return (input_header_count_);
58 }
59 
60 void
61 VersionedCSVFile::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 
71 void
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 
89 bool
91  return (input_schema_state_ != CURRENT);
92 }
93 
94 std::string
96  if (getValidColumnCount() > 0) {
97  return (getVersionedColumn(getValidColumnCount() - 1)->version_);
98  }
99 
100  return ("undefined");
101 }
102 
103 std::string
105  if (getColumnCount() > 0) {
106  return (getVersionedColumn(getColumnCount() - 1)->version_);
107  }
108 
109  return ("undefined");
110 }
111 
112 const VersionedColumnPtr&
113 VersionedCSVFile::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 
123 bool
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 
181 void
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 
191 bool
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
size_t getMinimumValidColumns() const
Returns the minimum number of columns which must be present for the file to be considered valid...
virtual ~VersionedCSVFile()
Destructor.
virtual void recreate()
Creates a new CSV file.
Definition: csv_file.cc:370
size_t getColumnCount() const
Returns the number of columns in the file.
Definition: csv_file.h:403
size_t getInputHeaderCount() const
Returns the number of columns found in the input header.
static CSVRow EMPTY_ROW()
Represents empty row.
Definition: csv_file.h:491
InputSchemaState
Possible input file schema states.
std::string getFilename() const
Returns the path to the CSV file.
Definition: csv_file.h:408
void trim(const size_t count)
Trims a given number of elements from the end of a row.
Definition: csv_file.cc:95
Contains the metadata for a single column in a file.
std::string getSchemaVersion() const
text version of current schema supported by the file&#39;s metadata
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
virtual void open(const bool seek_to_end=false)
Opens existing file or creates a new one.
virtual bool validateHeader(const CSVRow &header)
Validates the header of a VersionedCSVFile.
virtual void recreate()
Creates a new CSV file.
size_t getValuesCount() const
Returns number of values in a CSV row.
Definition: csv_file.h:85
boost::shared_ptr< VersionedColumn > VersionedColumnPtr
Defines a smart pointer to VersionedColumn.
void setMinimumValidColumns(const std::string &column_name)
Sets the minimum number of valid columns based on a given column.
int version()
returns Kea hooks version.
Represents a single row of the CSV file.
Definition: csv_file.h:51
Exception thrown when an error occurs during CSV file processing.
void setReadMsg(const std::string &read_msg)
Sets error message after row validation.
Definition: csv_file.h:486
std::string getInputSchemaVersion() const
Returns the schema version of the physical file.
bool needsConversion() const
Returns true if the input file schema state is not CURRENT.
Defines the logger used by the top-level component of kea-lfc.
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.
void addColumn(const std::string &col_name)
Adds new column name.
Definition: csv_file.cc:147
bool next(CSVRow &row)
Reads next row from the file file.
std::string getColumnName(const size_t col_index) const
Returns the name of the column.
Definition: csv_file.cc:247
enum InputSchemaState getInputSchemaState() const
Fetches the state of the input file&#39;s schema.
size_t getColumnIndex(const std::string &col_name) const
Returns the index of the column having specified name.
Definition: csv_file.cc:237
std::string readAt(const size_t at) const
Retrieves a value from the internal container.
Definition: csv_file.cc:60
Provides input/output access to CSV files.
Definition: csv_file.h:358
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
bool next(CSVRow &row, const bool skip_validation=false)
Reads next row from CSV file.
Definition: csv_file.cc:257
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...
VersionedCSVFile(const std::string &filename)
Constructor.
void append(const T value)
Appends the value as a new column.
Definition: csv_file.h:220
const VersionedColumnPtr & getVersionedColumn(const size_t index) const
Fetch the column descriptor for a given index.
virtual void open(const bool seek_to_end=false)
Opens existing file or creates a new one.
Definition: csv_file.cc:302