Kea  1.9.9-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
std::string getInputSchemaVersion() const
Returns the schema version of the physical file.
size_t getInputHeaderCount() const
Returns the number of columns found in the input header.
bool needsConversion() const
Returns true if the input file schema state is not CURRENT.
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
static CSVRow EMPTY_ROW()
Represents empty row.
Definition: csv_file.h:491
InputSchemaState
Possible input file schema states.
size_t getMinimumValidColumns() const
Returns the minimum number of columns which must be present for the file to be considered valid...
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 getSchemaVersion() const
text version of current schema supported by the file's metadata
Contains the metadata for a single column in a file.
std::string readAt(const size_t at) const
Retrieves a value from the internal container.
Definition: csv_file.cc:60
#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.
size_t getValuesCount() const
Returns number of values in a CSV row.
Definition: csv_file.h:85
virtual bool validateHeader(const CSVRow &header)
Validates the header of a VersionedCSVFile.
virtual void recreate()
Creates a new CSV file.
size_t getValidColumnCount() const
Returns the number of valid columns found in the header For newly created files this will always matc...
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.
std::string getColumnName(const size_t col_index) const
Returns the name of the column.
Definition: csv_file.cc:247
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
size_t getColumnIndex(const std::string &col_name) const
Returns the index of the column having specified name.
Definition: csv_file.cc:237
const VersionedColumnPtr & getVersionedColumn(const size_t index) const
Fetch the column descriptor for a given index.
enum InputSchemaState getInputSchemaState() const
Fetches the state of the input file's schema.
Defines the logger used by the top-level component of kea-dhcp-ddns.
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.
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.
VersionedCSVFile(const std::string &filename)
Constructor.
std::string getFilename() const
Returns the path to the CSV file.
Definition: csv_file.h:408
void append(const T value)
Appends the value as a new column.
Definition: csv_file.h:220
virtual void open(const bool seek_to_end=false)
Opens existing file or creates a new one.
Definition: csv_file.cc:302