parsing.cc 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "sql/recover_module/parsing.h"
  5. #include <cstddef>
  6. #include <ostream>
  7. #include <tuple>
  8. #include <utility>
  9. #include "base/check.h"
  10. #include "base/notreached.h"
  11. #include "base/strings/strcat.h"
  12. #include "base/strings/string_piece.h"
  13. #include "sql/recover_module/record.h"
  14. #include "third_party/abseil-cpp/absl/types/optional.h"
  15. namespace sql {
  16. namespace recover {
  17. namespace {
  18. // This module defines whitespace as space (0x20).
  19. constexpr bool IsWhiteSpace(char character) {
  20. return (character == ' ');
  21. }
  22. // Splits a token out of a SQL string representing a column type.
  23. //
  24. // Tokens are separated by space (0x20) characters.
  25. //
  26. // The SQL must not start with a space character.
  27. //
  28. // Returns the token and the rest of the SQL string. Consumes the space after
  29. // the returned token -- the rest of the SQL string will not start with space.
  30. std::pair<base::StringPiece, base::StringPiece> SplitToken(
  31. base::StringPiece sql) {
  32. DCHECK(sql.empty() || !IsWhiteSpace(sql[0]));
  33. size_t token_end = 0;
  34. while (token_end < sql.length() && !IsWhiteSpace(sql[token_end]))
  35. ++token_end;
  36. size_t split = token_end;
  37. while (split < sql.length() && IsWhiteSpace(sql[split]))
  38. ++split;
  39. return {sql.substr(0, token_end), sql.substr(split)};
  40. }
  41. // Column types.
  42. constexpr base::StringPiece kIntegerSql("INTEGER");
  43. constexpr base::StringPiece kFloatSql("FLOAT");
  44. constexpr base::StringPiece kTextSql("TEXT");
  45. constexpr base::StringPiece kBlobSql("BLOB");
  46. constexpr base::StringPiece kNumericSql("NUMERIC");
  47. constexpr base::StringPiece kRowidSql("ROWID");
  48. constexpr base::StringPiece kAnySql("ANY");
  49. // SQL keywords recognized by the recovery module.
  50. constexpr base::StringPiece kStrictSql("STRICT");
  51. constexpr base::StringPiece kNonNullSql1("NOT");
  52. constexpr base::StringPiece kNonNullSql2("NULL");
  53. absl::optional<ModuleColumnType> ParseColumnType(
  54. base::StringPiece column_type_sql) {
  55. if (column_type_sql == kIntegerSql)
  56. return ModuleColumnType::kInteger;
  57. if (column_type_sql == kFloatSql)
  58. return ModuleColumnType::kFloat;
  59. if (column_type_sql == kTextSql)
  60. return ModuleColumnType::kText;
  61. if (column_type_sql == kBlobSql)
  62. return ModuleColumnType::kBlob;
  63. if (column_type_sql == kNumericSql)
  64. return ModuleColumnType::kNumeric;
  65. if (column_type_sql == kRowidSql)
  66. return ModuleColumnType::kRowId;
  67. if (column_type_sql == kAnySql)
  68. return ModuleColumnType::kAny;
  69. return absl::nullopt;
  70. }
  71. // Returns a view into a SQL string representing the column type.
  72. //
  73. // The backing string is guaranteed to live for as long as the process runs.
  74. base::StringPiece ColumnTypeToSql(ModuleColumnType column_type) {
  75. switch (column_type) {
  76. case ModuleColumnType::kInteger:
  77. return kIntegerSql;
  78. case ModuleColumnType::kFloat:
  79. return kFloatSql;
  80. case ModuleColumnType::kText:
  81. return kTextSql;
  82. case ModuleColumnType::kBlob:
  83. return kBlobSql;
  84. case ModuleColumnType::kNumeric:
  85. return kNumericSql;
  86. case ModuleColumnType::kRowId:
  87. return kIntegerSql; // rowids are ints.
  88. case ModuleColumnType::kAny:
  89. return base::StringPiece();
  90. }
  91. NOTREACHED();
  92. }
  93. } // namespace
  94. std::string RecoveredColumnSpec::ToCreateTableSql() const {
  95. base::StringPiece not_null_sql = (is_non_null) ? " NOT NULL" : "";
  96. return base::StrCat(
  97. {this->name, " ", ColumnTypeToSql(this->type), not_null_sql});
  98. }
  99. bool RecoveredColumnSpec::IsAcceptableValue(ValueType value_type) const {
  100. if (value_type == ValueType::kNull)
  101. return !is_non_null || type == ModuleColumnType::kRowId;
  102. if (type == ModuleColumnType::kAny)
  103. return true;
  104. if (value_type == ValueType::kInteger) {
  105. return type == ModuleColumnType::kInteger ||
  106. (!is_strict && type == ModuleColumnType::kFloat);
  107. }
  108. if (value_type == ValueType::kFloat) {
  109. return type == ModuleColumnType::kFloat ||
  110. (!is_strict && type == ModuleColumnType::kFloat);
  111. }
  112. if (value_type == ValueType::kText)
  113. return type == ModuleColumnType::kText;
  114. if (value_type == ValueType::kBlob) {
  115. return type == ModuleColumnType::kBlob ||
  116. (!is_strict && type == ModuleColumnType::kText);
  117. }
  118. NOTREACHED() << "Unimplemented value type";
  119. return false;
  120. }
  121. RecoveredColumnSpec ParseColumnSpec(const char* sqlite_arg) {
  122. // The result is invalid until its |name| member is set.
  123. RecoveredColumnSpec result;
  124. auto [column_name, sql] = SplitToken(sqlite_arg);
  125. if (column_name.empty()) {
  126. // Empty column names are invalid.
  127. DCHECK(!result.IsValid());
  128. return result;
  129. }
  130. base::StringPiece column_type_sql;
  131. std::tie(column_type_sql, sql) = SplitToken(sql);
  132. absl::optional<ModuleColumnType> column_type =
  133. ParseColumnType(column_type_sql);
  134. if (!column_type.has_value()) {
  135. // Invalid column type.
  136. DCHECK(!result.IsValid());
  137. return result;
  138. }
  139. result.type = column_type.value();
  140. // Consume keywords.
  141. result.is_non_null = result.type == ModuleColumnType::kRowId;
  142. while (!sql.empty()) {
  143. base::StringPiece token;
  144. std::tie(token, sql) = SplitToken(sql);
  145. if (token == kStrictSql) {
  146. if (result.type == ModuleColumnType::kAny) {
  147. // STRICT is incompatible with ANY.
  148. DCHECK(!result.IsValid());
  149. return result;
  150. }
  151. result.is_strict = true;
  152. continue;
  153. }
  154. if (token != kNonNullSql1) {
  155. // Invalid SQL keyword.
  156. DCHECK(!result.IsValid());
  157. return result;
  158. }
  159. std::tie(token, sql) = SplitToken(sql);
  160. if (token != kNonNullSql2) {
  161. // Invalid SQL keyword.
  162. DCHECK(!result.IsValid());
  163. return result;
  164. }
  165. result.is_non_null = true;
  166. }
  167. result.name = std::string(column_name);
  168. return result;
  169. }
  170. TargetTableSpec ParseTableSpec(const char* sqlite_arg) {
  171. base::StringPiece sql(sqlite_arg);
  172. size_t separator_pos = sql.find('.');
  173. if (separator_pos == base::StringPiece::npos) {
  174. // The default attachment point name is "main".
  175. return TargetTableSpec{"main", sqlite_arg};
  176. }
  177. if (separator_pos == 0) {
  178. // Empty attachment point names are invalid.
  179. return TargetTableSpec();
  180. }
  181. base::StringPiece db_name = sql.substr(0, separator_pos);
  182. base::StringPiece table_name = sql.substr(separator_pos + 1);
  183. return TargetTableSpec{std::string(db_name), std::string(table_name)};
  184. }
  185. } // namespace recover
  186. } // namespace sql