OmniSciDB  085a039ca4
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DdlUtils.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2020 OmniSci, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "DdlUtils.h"
18 
19 #include <unordered_set>
20 
21 #include <boost/algorithm/string.hpp>
22 #include <boost/filesystem.hpp>
23 #include <boost/program_options.hpp>
24 
25 #include "rapidjson/document.h"
26 
28 #include "Geospatial/Types.h"
30 #include "Shared/SysDefinitions.h"
31 #include "Shared/file_path_util.h"
32 #include "Shared/misc.h"
33 #include "Shared/sqltypes.h"
34 
36 
37 namespace ddl_utils {
38 SqlType::SqlType(SQLTypes type, int param1, int param2, bool is_array, int array_size)
39  : type(type)
40  , param1(param1)
41  , param2(param2)
42  , is_array(is_array)
43  , array_size(array_size) {}
44 
46  return type;
47 }
48 
49 int SqlType::get_param1() const {
50  return param1;
51 }
52 
53 void SqlType::set_param1(int param) {
54  param1 = param;
55 }
56 
57 int SqlType::get_param2() const {
58  return param2;
59 }
60 
61 bool SqlType::get_is_array() const {
62  return is_array;
63 }
64 
66  is_array = a;
67 }
68 
70  return array_size;
71 }
72 
74  array_size = s;
75 }
76 
77 std::string SqlType::to_string() const {
78  std::string str;
79  switch (type) {
80  case kBOOLEAN:
81  str = "BOOLEAN";
82  break;
83  case kCHAR:
84  str = "CHAR(" + boost::lexical_cast<std::string>(param1) + ")";
85  break;
86  case kVARCHAR:
87  str = "VARCHAR(" + boost::lexical_cast<std::string>(param1) + ")";
88  break;
89  case kTEXT:
90  str = "TEXT";
91  break;
92  case kNUMERIC:
93  str = "NUMERIC(" + boost::lexical_cast<std::string>(param1);
94  if (param2 > 0) {
95  str += ", " + boost::lexical_cast<std::string>(param2);
96  }
97  str += ")";
98  break;
99  case kDECIMAL:
100  str = "DECIMAL(" + boost::lexical_cast<std::string>(param1);
101  if (param2 > 0) {
102  str += ", " + boost::lexical_cast<std::string>(param2);
103  }
104  str += ")";
105  break;
106  case kBIGINT:
107  str = "BIGINT";
108  break;
109  case kINT:
110  str = "INT";
111  break;
112  case kTINYINT:
113  str = "TINYINT";
114  break;
115  case kSMALLINT:
116  str = "SMALLINT";
117  break;
118  case kFLOAT:
119  str = "FLOAT";
120  break;
121  case kDOUBLE:
122  str = "DOUBLE";
123  break;
124  case kTIME:
125  str = "TIME";
126  if (param1 < 6) {
127  str += "(" + boost::lexical_cast<std::string>(param1) + ")";
128  }
129  break;
130  case kTIMESTAMP:
131  str = "TIMESTAMP";
132  if (param1 <= 9) {
133  str += "(" + boost::lexical_cast<std::string>(param1) + ")";
134  }
135  break;
136  case kDATE:
137  str = "DATE";
138  break;
139  default:
140  assert(false);
141  break;
142  }
143  if (is_array) {
144  str += "[";
145  if (array_size > 0) {
146  str += boost::lexical_cast<std::string>(array_size);
147  }
148  str += "]";
149  }
150  return str;
151 }
152 
154  switch (type) {
155  case kCHAR:
156  case kVARCHAR:
157  if (param1 <= 0) {
158  throw std::runtime_error("CHAR and VARCHAR must have a positive dimension.");
159  }
160  break;
161  case kDECIMAL:
162  case kNUMERIC:
163  if (param1 <= 0) {
164  throw std::runtime_error("DECIMAL and NUMERIC must have a positive precision.");
165  } else if (param1 > 19) {
166  throw std::runtime_error(
167  "DECIMAL and NUMERIC precision cannot be larger than 19.");
168  } else if (param1 <= param2) {
169  throw std::runtime_error(
170  "DECIMAL and NUMERIC must have precision larger than scale.");
171  }
172  break;
173  case kTIMESTAMP:
174  if (param1 == -1) {
175  param1 = 0; // set default to 0
176  } else if (param1 != 0 && param1 != 3 && param1 != 6 &&
177  param1 != 9) { // support ms, us, ns
178  throw std::runtime_error(
179  "Only TIMESTAMP(n) where n = (0,3,6,9) are supported now.");
180  }
181  break;
182  case kTIME:
183  if (param1 == -1) {
184  param1 = 0; // default precision is 0
185  }
186  if (param1 > 0) { // @TODO(wei) support sub-second precision later.
187  throw std::runtime_error("Only TIME(0) is supported now.");
188  }
189  break;
190  case kPOINT:
191  case kLINESTRING:
192  case kPOLYGON:
193  case kMULTIPOLYGON:
194  // Storing SRID in param1
195  break;
196  default:
197  param1 = 0;
198  break;
199  }
200 }
201 
202 Encoding::Encoding(std::string* encoding_name, int encoding_param)
203  : encoding_name(encoding_name), encoding_param(encoding_param) {}
204 
205 const std::string* Encoding::get_encoding_name() const {
206  return encoding_name.get();
207 }
208 
210  return encoding_param;
211 }
212 
214  // Change default TEXT column behaviour to be DICT encoded
215  if (cd.columnType.is_string() || cd.columnType.is_string_array()) {
216  // default to 32-bits
218  cd.columnType.set_comp_param(32);
219  } else if (cd.columnType.is_decimal() && cd.columnType.get_precision() <= 4) {
221  cd.columnType.set_comp_param(16);
222  } else if (cd.columnType.is_decimal() && cd.columnType.get_precision() <= 9) {
224  cd.columnType.set_comp_param(32);
225  } else if (cd.columnType.is_decimal() && cd.columnType.get_precision() > 18) {
226  throw std::runtime_error(cd.columnName + ": Precision too high, max 18.");
227  } else if (cd.columnType.is_geometry() && cd.columnType.get_output_srid() == 4326) {
228  // default to GEOINT 32-bits
230  cd.columnType.set_comp_param(32);
232  // Days encoding for DATE
235  } else {
238  }
239 }
240 
242  int encoding_size,
243  const SqlType* column_type) {
244  auto type = cd.columnType.get_type();
245  // fixed-bits encoding
246  if (type == kARRAY) {
247  type = cd.columnType.get_subtype();
248  switch (type) {
249  case kTINYINT:
250  case kSMALLINT:
251  case kINT:
252  case kBIGINT:
253  case kDATE:
254  throw std::runtime_error(cd.columnName + ": Cannot apply FIXED encoding to " +
255  column_type->to_string() + " type array.");
256  break;
257  default:
258  break;
259  }
260  }
261 
262  if (!IS_INTEGER(type) && !is_datetime(type) &&
263  !(type == kDECIMAL || type == kNUMERIC)) {
264  throw std::runtime_error(
265  cd.columnName +
266  ": Fixed encoding is only supported for integer or time columns.");
267  }
268 
269  switch (type) {
270  case kSMALLINT:
271  if (encoding_size != 8) {
272  throw std::runtime_error(
273  cd.columnName +
274  ": Compression parameter for Fixed encoding on SMALLINT must be 8.");
275  }
276  break;
277  case kINT:
278  if (encoding_size != 8 && encoding_size != 16) {
279  throw std::runtime_error(
280  cd.columnName +
281  ": Compression parameter for Fixed encoding on INTEGER must be 8 or 16.");
282  }
283  break;
284  case kBIGINT:
285  if (encoding_size != 8 && encoding_size != 16 && encoding_size != 32) {
286  throw std::runtime_error(cd.columnName +
287  ": Compression parameter for Fixed encoding on "
288  "BIGINT must be 8 or 16 or 32.");
289  }
290  break;
291  case kTIMESTAMP:
292  case kTIME:
293  if (encoding_size != 32) {
294  throw std::runtime_error(cd.columnName +
295  ": Compression parameter for Fixed encoding on "
296  "TIME or TIMESTAMP must be 32.");
297  } else if (cd.columnType.is_high_precision_timestamp()) {
298  throw std::runtime_error("Fixed encoding is not supported for TIMESTAMP(3|6|9).");
299  }
300  break;
301  case kDECIMAL:
302  case kNUMERIC:
303  if (encoding_size != 32 && encoding_size != 16) {
304  throw std::runtime_error(cd.columnName +
305  ": Compression parameter for Fixed encoding on "
306  "DECIMAL must be 16 or 32.");
307  }
308 
309  if (encoding_size == 32 && cd.columnType.get_precision() > 9) {
310  throw std::runtime_error(cd.columnName +
311  ": Precision too high for Fixed(32) encoding, max 9.");
312  }
313 
314  if (encoding_size == 16 && cd.columnType.get_precision() > 4) {
315  throw std::runtime_error(cd.columnName +
316  ": Precision too high for Fixed(16) encoding, max 4.");
317  }
318  break;
319  case kDATE:
320  if (encoding_size != 32 && encoding_size != 16) {
321  throw std::runtime_error(cd.columnName +
322  ": Compression parameter for Fixed encoding on "
323  "DATE must be 16 or 32.");
324  }
325  break;
326  default:
327  throw std::runtime_error(cd.columnName + ": Cannot apply FIXED encoding to " +
328  column_type->to_string());
329  }
330  if (type == kDATE) {
332  cd.columnType.set_comp_param(16);
333  } else {
335  cd.columnType.set_comp_param(encoding_size);
336  }
337 }
338 
340  if (!cd.columnType.is_string() && !cd.columnType.is_string_array()) {
341  throw std::runtime_error(
342  cd.columnName +
343  ": Dictionary encoding is only supported on string or string array columns.");
344  }
345  int comp_param;
346  if (encoding_size == 0) {
347  comp_param = 32; // default to 32-bits
348  } else {
349  comp_param = encoding_size;
350  }
351  if (cd.columnType.is_string_array() && comp_param != 32) {
352  throw std::runtime_error(cd.columnName +
353  ": Compression parameter for string arrays must be 32");
354  }
355  if (comp_param != 8 && comp_param != 16 && comp_param != 32) {
356  throw std::runtime_error(
357  cd.columnName +
358  ": Compression parameter for Dictionary encoding must be 8 or 16 or 32.");
359  }
360  // dictionary encoding
362  cd.columnType.set_comp_param(comp_param);
363 }
364 
366  if (!cd.columnType.is_string() && !cd.columnType.is_string_array() &&
367  !cd.columnType.is_geometry()) {
368  throw std::runtime_error(
369  cd.columnName +
370  ": None encoding is only supported on string, string array, or geo columns.");
371  }
374 }
375 
377  // sparse column encoding with mostly NULL values
378  if (cd.columnType.get_notnull()) {
379  throw std::runtime_error(cd.columnName +
380  ": Cannot do sparse column encoding on a NOT NULL column.");
381  }
382  if (encoding_size == 0 || encoding_size % 8 != 0 || encoding_size > 48) {
383  throw std::runtime_error(
384  cd.columnName +
385  "Must specify number of bits as 8, 16, 24, 32 or 48 as the parameter to "
386  "sparse-column encoding.");
387  }
389  cd.columnType.set_comp_param(encoding_size);
390  // throw std::runtime_error("SPARSE encoding not supported yet.");
391 }
392 
394  if (!cd.columnType.is_geometry() || cd.columnType.get_output_srid() != 4326) {
395  throw std::runtime_error(
396  cd.columnName + ": COMPRESSED encoding is only supported on WGS84 geo columns.");
397  }
398  int comp_param;
399  if (encoding_size == 0) {
400  comp_param = 32; // default to 32-bits
401  } else {
402  comp_param = encoding_size;
403  }
404  if (comp_param != 32) {
405  throw std::runtime_error(cd.columnName +
406  ": only 32-bit COMPRESSED geo encoding is supported");
407  }
408  // encoding longitude/latitude as integers
410  cd.columnType.set_comp_param(comp_param);
411 }
412 
413 void validate_and_set_date_encoding(ColumnDescriptor& cd, int encoding_size) {
414  // days encoding for dates
415  if (cd.columnType.get_type() == kARRAY && cd.columnType.get_subtype() == kDATE) {
416  throw std::runtime_error(cd.columnName +
417  ": Cannot apply days encoding to date array.");
418  }
419  if (cd.columnType.get_type() != kDATE) {
420  throw std::runtime_error(cd.columnName +
421  ": Days encoding is only supported for DATE columns.");
422  }
423  if (encoding_size != 32 && encoding_size != 16) {
424  throw std::runtime_error(cd.columnName +
425  ": Compression parameter for Days encoding on "
426  "DATE must be 16 or 32.");
427  }
429  cd.columnType.set_comp_param((encoding_size == 16) ? 16 : 0);
430 }
431 
433  const Encoding* encoding,
434  const SqlType* column_type) {
435  if (encoding == nullptr) {
437  } else {
438  const std::string& comp = *encoding->get_encoding_name();
439  if (boost::iequals(comp, "fixed")) {
440  validate_and_set_fixed_encoding(cd, encoding->get_encoding_param(), column_type);
441  } else if (boost::iequals(comp, "rl")) {
442  // run length encoding
445  // throw std::runtime_error("RL(Run Length) encoding not supported yet.");
446  } else if (boost::iequals(comp, "diff")) {
447  // differential encoding
450  // throw std::runtime_error("DIFF(differential) encoding not supported yet.");
451  } else if (boost::iequals(comp, "dict")) {
453  } else if (boost::iequals(comp, "NONE")) {
455  } else if (boost::iequals(comp, "sparse")) {
457  } else if (boost::iequals(comp, "compressed")) {
459  } else if (boost::iequals(comp, "days")) {
461  } else {
462  throw std::runtime_error(cd.columnName + ": Invalid column compression scheme " +
463  comp);
464  }
465  }
466 }
467 
469  column_type->check_type();
470 
471  if (column_type->get_is_array()) {
473  cd.columnType.set_subtype(column_type->get_type());
474  } else {
475  cd.columnType.set_type(column_type->get_type());
476  }
477  if (IS_GEO(column_type->get_type())) {
478  cd.columnType.set_subtype(static_cast<SQLTypes>(column_type->get_param1()));
479  cd.columnType.set_input_srid(column_type->get_param2());
480  cd.columnType.set_output_srid(column_type->get_param2());
481  } else {
482  cd.columnType.set_dimension(column_type->get_param1());
483  cd.columnType.set_scale(column_type->get_param2());
484  }
485 }
486 
487 void validate_and_set_array_size(ColumnDescriptor& cd, const SqlType* column_type) {
488  if (cd.columnType.is_string_array() &&
490  throw std::runtime_error(
491  cd.columnName +
492  ": Array of strings must be dictionary encoded. Specify ENCODING DICT");
493  }
494 
495  if (column_type->get_is_array()) {
496  int s = -1;
497  auto array_size = column_type->get_array_size();
498  if (array_size > 0) {
499  auto sti = cd.columnType.get_elem_type();
500  s = array_size * sti.get_size();
501  if (s <= 0) {
502  throw std::runtime_error(cd.columnName + ": Unexpected fixed length array size");
503  }
504  }
505  cd.columnType.set_size(s);
506 
507  } else {
509  }
510 }
511 
512 namespace {
513 
514 void validate_literal(const std::string& val,
515  SQLTypeInfo column_type,
516  const std::string& column_name) {
517  if (to_upper(val) == "NULL") {
518  return;
519  }
520  switch (column_type.get_type()) {
521  case kBOOLEAN:
522  case kTINYINT:
523  case kSMALLINT:
524  case kINT:
525  case kBIGINT:
526  case kFLOAT:
527  case kDOUBLE:
528  case kTIME:
529  case kTIMESTAMP:
530  StringToDatum(val, column_type);
531  break;
532  case kDATE: {
533  auto d = StringToDatum(val, column_type);
534  DateDaysOverflowValidator validator(column_type);
535  validator.validate(d.bigintval);
536  break;
537  }
538  case kDECIMAL:
539  case kNUMERIC: {
540  SQLTypeInfo ti(kNUMERIC, 0, 0, false);
541  auto d = StringToDatum(val, ti);
542  auto converted_val = convert_decimal_value_to_scale(d.bigintval, ti, column_type);
543  DecimalOverflowValidator validator(column_type);
544  validator.validate(converted_val);
545  break;
546  }
547  case kTEXT:
548  case kVARCHAR:
549  case kCHAR:
550  if (val.length() > StringDictionary::MAX_STRLEN) {
551  throw std::runtime_error("String too long for column " + column_name + " was " +
552  std::to_string(val.length()) + " max is " +
554  }
555  break;
556  case kARRAY: {
557  if (val.front() != '{' || val.back() != '}') {
558  throw std::runtime_error(column_name +
559  ": arrays should start and end with curly braces");
560  }
561  std::vector<std::string> elements = split(val.substr(1, val.length() - 2), ", ");
562  if (column_type.get_size() > 0) {
563  auto sti = column_type.get_elem_type();
564  size_t expected_size = column_type.get_size() / sti.get_size();
565  size_t actual_size = elements.size();
566  if (actual_size != expected_size) {
567  throw std::runtime_error("Fixed length array column " + column_name +
568  " expects " + std::to_string(expected_size) +
569  " values, received " + std::to_string(actual_size));
570  }
571  }
572  SQLTypeInfo element_ti = column_type.get_elem_type();
573  for (const auto& element : elements) {
574  if (to_upper(element) != "NULL") {
575  validate_literal(element, element_ti, column_name);
576  }
577  }
578  break;
579  }
580  case kPOINT:
581  case kLINESTRING:
582  case kPOLYGON:
583  case kMULTIPOLYGON:
584  if (val.empty()) {
585  return;
586  }
587  try {
589  if (!geo) {
590  throw std::runtime_error("Unexpected geo literal '" + val + "' for column " +
591  column_name);
592  }
593  if (!geo->transform(column_type)) {
594  throw std::runtime_error("Cannot transform SRID for literal '" + val +
595  "' for column " + column_name);
596  } else {
597  auto sql_type = column_type.get_type();
598  auto geo_type = geo->getType();
599  if ((geo_type == Geospatial::GeoBase::GeoType::kPOINT && sql_type != kPOINT) ||
601  sql_type != kLINESTRING) ||
603  sql_type != kPOLYGON) ||
605  sql_type != kMULTIPOLYGON)) {
606  throw std::runtime_error("Geo literal '" + val +
607  "' doesn't match the type "
608  "of column column " +
609  column_name);
610  }
611  }
612  } catch (Geospatial::GeoTypesError& e) {
613  throw std::runtime_error("Unexpected geo literal '" + val + "' for column " +
614  column_name + ": " + e.what());
615  }
616  break;
617  default:
618  CHECK(false) << "validate_literal() does not support type "
619  << column_type.get_type();
620  }
621 }
622 
623 } // namespace
624 
626  const std::string* default_value,
627  bool not_null) {
628  bool is_null_literal =
629  default_value && ((to_upper(*default_value) == "NULL") ||
630  (cd.columnType.is_geometry() && default_value->empty()));
631  if (not_null && (is_null_literal)) {
632  throw std::runtime_error(cd.columnName +
633  ": cannot set default value to NULL for "
634  "NOT NULL column");
635  }
636  if (!default_value || is_null_literal) {
637  cd.default_value = std::nullopt;
638  return;
639  }
640  const auto& column_type = cd.columnType;
641  const auto& val = *default_value;
642  validate_literal(val, column_type, cd.columnName);
643  cd.default_value = std::make_optional(*default_value);
644 }
645 
646 void set_column_descriptor(const std::string& column_name,
647  ColumnDescriptor& cd,
648  SqlType* column_type,
649  const bool not_null,
650  const Encoding* encoding,
651  const std::string* default_value) {
652  cd.columnName = column_name;
653  validate_and_set_type(cd, column_type);
654  cd.columnType.set_notnull(not_null);
655  validate_and_set_encoding(cd, encoding, column_type);
656  validate_and_set_array_size(cd, column_type);
657  cd.isSystemCol = false;
658  cd.isVirtualCol = false;
659  validate_and_set_default_value(cd, default_value, not_null);
660 }
661 
662 void set_default_table_attributes(const std::string& table_name,
663  TableDescriptor& td,
664  const int32_t column_count) {
665  td.tableName = table_name;
666  td.nColumns = column_count;
667  td.isView = false;
668  td.fragmenter = nullptr;
674 }
675 
676 void validate_non_duplicate_column(const std::string& column_name,
677  std::unordered_set<std::string>& upper_column_names) {
678  const auto upper_column_name = boost::to_upper_copy<std::string>(column_name);
679  const auto insert_it = upper_column_names.insert(upper_column_name);
680  if (!insert_it.second) {
681  throw std::runtime_error("Column '" + column_name + "' defined more than once");
682  }
683 }
684 
685 void validate_non_reserved_keyword(const std::string& column_name) {
686  const auto upper_column_name = boost::to_upper_copy<std::string>(column_name);
687  if (reserved_keywords.find(upper_column_name) != reserved_keywords.end()) {
688  throw std::runtime_error("Cannot create column with reserved keyword '" +
689  column_name + "'");
690  }
691 }
692 
694  const TableType expected_table_type,
695  const std::string& command) {
696  if (td->isView) {
697  if (expected_table_type != TableType::VIEW) {
698  throw std::runtime_error(td->tableName + " is a view. Use " + command + " VIEW.");
699  }
700  } else if (td->storageType == StorageType::FOREIGN_TABLE) {
701  if (expected_table_type != TableType::FOREIGN_TABLE) {
702  throw std::runtime_error(td->tableName + " is a foreign table. Use " + command +
703  " FOREIGN TABLE.");
704  }
705  } else if (expected_table_type != TableType::TABLE) {
706  throw std::runtime_error(td->tableName + " is a table. Use " + command + " TABLE.");
707  }
708 }
709 
710 std::string table_type_enum_to_string(const TableType table_type) {
711  if (table_type == ddl_utils::TableType::TABLE) {
712  return "Table";
713  }
714  if (table_type == ddl_utils::TableType::FOREIGN_TABLE) {
715  return "ForeignTable";
716  }
717  if (table_type == ddl_utils::TableType::VIEW) {
718  return "View";
719  }
720  throw std::runtime_error{"Unexpected table type"};
721 }
722 
723 std::string get_malformed_config_error_message(const std::string& config_key) {
724  return "Configuration value for \"" + config_key +
725  "\" is malformed. Value should be a list of paths with format: [ "
726  "\"root-path-1\", \"root-path-2\", ... ]";
727 }
728 
729 void validate_expanded_file_path(const std::string& file_path,
730  const std::vector<std::string>& whitelisted_root_paths) {
731  const auto& canonical_file_path = boost::filesystem::canonical(file_path);
732  for (const auto& root_path : whitelisted_root_paths) {
733  if (boost::istarts_with(canonical_file_path.string(), root_path)) {
734  return;
735  }
736  }
737  if (canonical_file_path == boost::filesystem::absolute(file_path)) {
738  throw std::runtime_error{"File or directory path \"" + file_path +
739  "\" is not whitelisted."};
740  }
741  throw std::runtime_error{"File or directory path \"" + file_path +
742  "\" (resolved to \"" + canonical_file_path.string() +
743  "\") is not whitelisted."};
744 }
745 
746 std::vector<std::string> get_expanded_file_paths(
747  const std::string& file_path,
748  const DataTransferType data_transfer_type) {
749  std::vector<std::string> file_paths;
750  if (data_transfer_type == DataTransferType::IMPORT) {
752  file_path, std::nullopt, std::nullopt, std::nullopt);
753  } else {
754  std::string path;
755  if (!boost::filesystem::exists(file_path)) {
756  // For exports, it is possible to provide a path to a new (nonexistent) file. In
757  // this case, validate using the parent path.
758  path = boost::filesystem::path(file_path).parent_path().string();
759  if (!boost::filesystem::exists(path)) {
760  throw std::runtime_error{"File or directory \"" + file_path +
761  "\" does not exist."};
762  }
763  } else {
764  path = file_path;
765  }
766  file_paths = {path};
767  }
768  return file_paths;
769 }
770 
771 void validate_allowed_file_path(const std::string& file_path,
772  const DataTransferType data_transfer_type,
773  const bool allow_wildcards) {
774  // Reject any punctuation characters except for a few safe ones.
775  // Some punctuation characters present a security risk when passed
776  // to subprocesses. Don't change this without a security review.
777  static const std::string safe_punctuation{"./_+-=:~"};
778  for (const auto& ch : file_path) {
779  if (std::ispunct(ch) && safe_punctuation.find(ch) == std::string::npos &&
780  !(allow_wildcards && ch == '*')) {
781  throw std::runtime_error(std::string("Punctuation \"") + ch +
782  "\" is not allowed in file path: " + file_path);
783  }
784  }
785 
786  // Enforce our whitelist and blacklist for file paths.
787  const auto& expanded_file_paths =
788  get_expanded_file_paths(file_path, data_transfer_type);
789  for (const auto& path : expanded_file_paths) {
791  const auto& canonical_file_path = boost::filesystem::canonical(file_path);
792  if (canonical_file_path == boost::filesystem::absolute(file_path)) {
793  throw std::runtime_error{"Access to file or directory path \"" + file_path +
794  "\" is not allowed."};
795  }
796  throw std::runtime_error{"Access to file or directory path \"" + file_path +
797  "\" (resolved to \"" + canonical_file_path.string() +
798  "\") is not allowed."};
799  }
800  }
801  FilePathWhitelist::validateWhitelistedFilePath(expanded_file_paths, data_transfer_type);
802 }
803 
804 void set_whitelisted_paths(const std::string& config_key,
805  const std::string& config_value,
806  std::vector<std::string>& whitelisted_paths) {
807  rapidjson::Document whitelisted_root_paths;
808  whitelisted_root_paths.Parse(config_value);
809  if (!whitelisted_root_paths.IsArray()) {
810  throw std::runtime_error{get_malformed_config_error_message(config_key)};
811  }
812  for (const auto& root_path : whitelisted_root_paths.GetArray()) {
813  if (!root_path.IsString()) {
814  throw std::runtime_error{get_malformed_config_error_message(config_key)};
815  }
816  if (!boost::filesystem::exists(root_path.GetString())) {
817  throw std::runtime_error{"Whitelisted root path \"" +
818  std::string{root_path.GetString()} + "\" does not exist."};
819  }
820  whitelisted_paths.emplace_back(
821  boost::filesystem::canonical(root_path.GetString()).string());
822  }
823  LOG(INFO) << "Parsed " << config_key << ": "
824  << shared::printContainer(whitelisted_paths);
825 }
826 
827 void FilePathWhitelist::initialize(const std::string& data_dir,
828  const std::string& allowed_import_paths,
829  const std::string& allowed_export_paths) {
830  CHECK(!data_dir.empty());
831  CHECK(boost::filesystem::is_directory(data_dir));
832 
833  auto data_dir_path = boost::filesystem::canonical(data_dir);
835  whitelisted_import_paths_.emplace_back(
836  (data_dir_path / shared::kDefaultImportDirName).string());
837 
839  whitelisted_export_paths_.emplace_back(
840  (data_dir_path / shared::kDefaultExportDirName).string());
841 
842  if (!allowed_import_paths.empty()) {
844  "allowed-import-paths", allowed_import_paths, whitelisted_import_paths_);
845  }
846  if (!allowed_export_paths.empty()) {
848  "allowed-export-paths", allowed_export_paths, whitelisted_export_paths_);
849  }
850 }
851 
853  const std::vector<std::string>& expanded_file_paths,
854  const DataTransferType data_transfer_type) {
855  for (const auto& path : expanded_file_paths) {
856  if (data_transfer_type == DataTransferType::IMPORT) {
858  } else if (data_transfer_type == DataTransferType::EXPORT) {
860  } else {
861  UNREACHABLE();
862  }
863  }
864 }
865 
869 }
870 
871 std::vector<std::string> FilePathWhitelist::whitelisted_import_paths_{};
872 std::vector<std::string> FilePathWhitelist::whitelisted_export_paths_{};
873 
874 void FilePathBlacklist::addToBlacklist(const std::string& path) {
875  CHECK(!path.empty());
876  blacklisted_paths_.emplace_back(path);
877 }
878 
879 bool FilePathBlacklist::isBlacklistedPath(const std::string& path) {
880  const auto canonical_path = boost::filesystem::canonical(path).string();
881  for (const auto& blacklisted_path : blacklisted_paths_) {
882  std::string full_path;
883  try {
884  full_path = boost::filesystem::canonical(blacklisted_path).string();
885  } catch (...) {
892  full_path = boost::filesystem::absolute(blacklisted_path).string();
893  }
894  if (boost::istarts_with(canonical_path, full_path)) {
895  return true;
896  }
897  }
898  return false;
899 }
900 
902  blacklisted_paths_.clear();
903 }
904 
905 std::vector<std::string> FilePathBlacklist::blacklisted_paths_{};
906 } // namespace ddl_utils
static std::set< std::string > reserved_keywords
DataTransferType
Definition: DdlUtils.h:80
HOST DEVICE SQLTypes get_subtype() const
Definition: sqltypes.h:330
void set_compression(EncodingType c)
Definition: sqltypes.h:440
void set_size(int s)
Definition: sqltypes.h:437
static std::unique_ptr< GeoBase > createGeoType(const std::string &wkt_or_wkb_hex)
Definition: Types.cpp:919
void validate_and_set_sparse_encoding(ColumnDescriptor &cd, int encoding_size)
Definition: DdlUtils.cpp:376
std::vector< std::string > get_expanded_file_paths(const std::string &file_path, const DataTransferType data_transfer_type)
Definition: DdlUtils.cpp:746
static std::vector< std::string > whitelisted_export_paths_
Definition: DdlUtils.h:94
HOST DEVICE int get_size() const
Definition: sqltypes.h:339
void validate_literal(const std::string &val, SQLTypeInfo column_type, const std::string &column_name)
Definition: DdlUtils.cpp:514
shared utility for globbing files, paths can be specified as either a single file, directory or wildcards
Definition: sqltypes.h:49
SQLTypes
Definition: sqltypes.h:38
std::string tableName
SqlType(SQLTypes type, int param1, int param2, bool is_array, int array_size)
Definition: DdlUtils.cpp:38
void validate_and_set_array_size(ColumnDescriptor &cd, const SqlType *column_type)
Definition: DdlUtils.cpp:487
virtual void check_type()
Definition: DdlUtils.cpp:153
static void initialize(const std::string &data_dir, const std::string &allowed_import_paths, const std::string &allowed_export_paths)
Definition: DdlUtils.cpp:827
void validate_and_set_dictionary_encoding(ColumnDescriptor &cd, int encoding_size)
Definition: DdlUtils.cpp:339
#define LOG(tag)
Definition: Logger.h:217
std::string storageType
#define DEFAULT_MAX_CHUNK_SIZE
#define UNREACHABLE()
Definition: Logger.h:267
HOST DEVICE void set_subtype(SQLTypes st)
Definition: sqltypes.h:430
virtual int get_encoding_param() const
Definition: DdlUtils.cpp:209
Constants for Builtin SQL Types supported by OmniSci.
const std::string kDefaultExportDirName
HOST DEVICE SQLTypes get_type() const
Definition: sqltypes.h:329
std::string to_string(char const *&&v)
std::vector< std::string > split(std::string_view str, std::string_view delim, std::optional< size_t > maxsplit)
split apart a string into a vector of substrings
void validate_non_duplicate_column(const std::string &column_name, std::unordered_set< std::string > &upper_column_names)
Definition: DdlUtils.cpp:676
constexpr double a
Definition: Utm.h:32
void set_column_descriptor(const std::string &column_name, ColumnDescriptor &cd, SqlType *column_type, const bool not_null, const Encoding *encoding, const std::string *default_value)
Definition: DdlUtils.cpp:646
void validate_and_set_none_encoding(ColumnDescriptor &cd)
Definition: DdlUtils.cpp:365
void set_input_srid(int d)
Definition: sqltypes.h:433
void validate_and_set_encoding(ColumnDescriptor &cd, const Encoding *encoding, const SqlType *column_type)
Definition: DdlUtils.cpp:432
bool g_use_date_in_days_default_encoding
Definition: DdlUtils.cpp:35
#define DEFAULT_MAX_ROWS
const std::string kDefaultImportDirName
static std::vector< std::string > whitelisted_import_paths_
Definition: DdlUtils.h:93
static void validateWhitelistedFilePath(const std::vector< std::string > &expanded_file_paths, const DataTransferType data_transfer_type)
Definition: DdlUtils.cpp:852
void set_fixed_size()
Definition: sqltypes.h:438
void set_default_encoding(ColumnDescriptor &cd)
Definition: DdlUtils.cpp:213
void set_scale(int s)
Definition: sqltypes.h:434
SQLTypes type
Definition: DdlUtils.h:54
void validate(T value)
Definition: Encoder.h:122
virtual SQLTypes get_type() const
Definition: DdlUtils.cpp:45
void validate_expanded_file_path(const std::string &file_path, const std::vector< std::string > &whitelisted_root_paths)
Definition: DdlUtils.cpp:729
virtual std::string to_string() const
Definition: DdlUtils.cpp:77
void validate_non_reserved_keyword(const std::string &column_name)
Definition: DdlUtils.cpp:685
Datum StringToDatum(std::string_view s, SQLTypeInfo &ti)
Definition: Datum.cpp:275
specifies the content in-memory of a row in the column metadata table
void set_default_table_attributes(const std::string &table_name, TableDescriptor &td, const int32_t column_count)
Definition: DdlUtils.cpp:662
std::shared_ptr< Fragmenter_Namespace::AbstractFragmenter > fragmenter
int get_precision() const
Definition: sqltypes.h:332
void validate_allowed_file_path(const std::string &file_path, const DataTransferType data_transfer_type, const bool allow_wildcards)
Definition: DdlUtils.cpp:771
void set_output_srid(int s)
Definition: sqltypes.h:435
std::string to_upper(const std::string &str)
#define DEFAULT_PAGE_SIZE
void set_comp_param(int p)
Definition: sqltypes.h:441
void validate_and_set_compressed_encoding(ColumnDescriptor &cd, int encoding_size)
Definition: DdlUtils.cpp:393
std::optional< std::string > default_value
Definition: sqltypes.h:52
Definition: sqltypes.h:53
HOST DEVICE EncodingType get_compression() const
Definition: sqltypes.h:337
virtual const std::string * get_encoding_name() const
Definition: DdlUtils.cpp:205
std::vector< std::string > local_glob_filter_sort_files(const std::string &file_path, const std::optional< std::string > &filter_regex, const std::optional< std::string > &sort_by, const std::optional< std::string > &sort_regex, const bool recurse)
int64_t convert_decimal_value_to_scale(const int64_t decimal_value, const SQLTypeInfo &type_info, const SQLTypeInfo &new_type_info)
Definition: Datum.cpp:556
std::string get_malformed_config_error_message(const std::string &config_key)
Definition: DdlUtils.cpp:723
void set_dimension(int d)
Definition: sqltypes.h:431
#define DEFAULT_FRAGMENT_ROWS
void validate_and_set_fixed_encoding(ColumnDescriptor &cd, int encoding_size, const SqlType *column_type)
Definition: DdlUtils.cpp:241
std::string table_type_enum_to_string(const TableType table_type)
Definition: DdlUtils.cpp:710
Fragmenter_Namespace::FragmenterType fragType
Encoding(std::string *encoding_name, int encoding_param)
Definition: DdlUtils.cpp:202
#define IS_INTEGER(T)
Definition: sqltypes.h:245
void set_whitelisted_paths(const std::string &config_key, const std::string &config_value, std::vector< std::string > &whitelisted_paths)
Definition: DdlUtils.cpp:804
Definition: sqltypes.h:41
virtual void set_param1(int param)
Definition: DdlUtils.cpp:53
static bool isBlacklistedPath(const std::string &path)
Definition: DdlUtils.cpp:879
void validate_table_type(const TableDescriptor *td, const TableType expected_table_type, const std::string &command)
Definition: DdlUtils.cpp:693
void set_notnull(bool n)
Definition: sqltypes.h:436
#define CHECK(condition)
Definition: Logger.h:223
bool is_geometry() const
Definition: sqltypes.h:522
void validate_and_set_default_value(ColumnDescriptor &cd, const std::string *default_value, bool not_null)
Definition: DdlUtils.cpp:625
bool is_high_precision_timestamp() const
Definition: sqltypes.h:885
void validate_and_set_date_encoding(ColumnDescriptor &cd, int encoding_size)
Definition: DdlUtils.cpp:413
static constexpr size_t MAX_STRLEN
static void addToBlacklist(const std::string &path)
Definition: DdlUtils.cpp:874
Definition: sqltypes.h:45
std::unique_ptr< std::string > encoding_name
Definition: DdlUtils.h:76
SQLTypeInfo columnType
virtual void set_is_array(bool a)
Definition: DdlUtils.cpp:65
PrintContainer< CONTAINER > printContainer(CONTAINER &container)
Definition: misc.h:108
bool is_string() const
Definition: sqltypes.h:510
virtual int get_param1() const
Definition: DdlUtils.cpp:49
HOST DEVICE bool get_notnull() const
Definition: sqltypes.h:336
static constexpr char const * FOREIGN_TABLE
bool is_string_array() const
Definition: sqltypes.h:511
void validate(T value) const
Definition: Encoder.h:54
SQLTypeInfo get_elem_type() const
Definition: sqltypes.h:865
bool is_decimal() const
Definition: sqltypes.h:513
virtual int get_param2() const
Definition: DdlUtils.cpp:57
std::string columnName
#define IS_GEO(T)
Definition: sqltypes.h:251
virtual bool get_is_array() const
Definition: DdlUtils.cpp:61
HOST DEVICE int get_output_srid() const
Definition: sqltypes.h:335
virtual void set_array_size(int s)
Definition: DdlUtils.cpp:73
constexpr auto is_datetime(SQLTypes type)
Definition: sqltypes.h:263
static std::vector< std::string > blacklisted_paths_
Definition: DdlUtils.h:104
virtual int get_array_size() const
Definition: DdlUtils.cpp:69
void validate_and_set_type(ColumnDescriptor &cd, SqlType *column_type)
Definition: DdlUtils.cpp:468
HOST DEVICE void set_type(SQLTypes t)
Definition: sqltypes.h:429