souffle  2.0.2-371-g6315b36
ReadStreamSQLite.h
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved
4  * Licensed under the Universal Permissive License v 1.0 as shown at:
5  * - https://opensource.org/licenses/UPL
6  * - <souffle root>/licenses/SOUFFLE-UPL.txt
7  */
8 
9 /************************************************************************
10  *
11  * @file ReadStreamSQLite.h
12  *
13  ***********************************************************************/
14 
15 #pragma once
16 
17 #include "souffle/RamTypes.h"
18 #include "souffle/SymbolTable.h"
19 #include "souffle/io/ReadStream.h"
22 #include <cassert>
23 #include <cstdint>
24 #include <fstream>
25 #include <map>
26 #include <memory>
27 #include <stdexcept>
28 #include <string>
29 #include <vector>
30 #include <sqlite3.h>
31 
32 namespace souffle {
33 class RecordTable;
34 
35 class ReadStreamSQLite : public ReadStream {
36 public:
37  ReadStreamSQLite(const std::map<std::string, std::string>& rwOperation, SymbolTable& symbolTable,
38  RecordTable& recordTable)
39  : ReadStream(rwOperation, symbolTable, recordTable), dbFilename(getFileName(rwOperation)),
40  relationName(rwOperation.at("name")) {
41  openDB();
44  }
45 
46  ~ReadStreamSQLite() override {
47  sqlite3_finalize(selectStatement);
48  sqlite3_close(db);
49  }
50 
51 protected:
52  /**
53  * Read and return the next tuple.
54  *
55  * Returns nullptr if no tuple was readable.
56  * @return
57  */
58  Own<RamDomain[]> readNextTuple() override {
59  if (sqlite3_step(selectStatement) != SQLITE_ROW) {
60  return nullptr;
61  }
62 
63  Own<RamDomain[]> tuple = std::make_unique<RamDomain[]>(arity + auxiliaryArity);
64 
65  uint32_t column;
66  for (column = 0; column < arity; column++) {
67  std::string element(reinterpret_cast<const char*>(sqlite3_column_text(selectStatement, column)));
68 
69  if (element.empty()) {
70  element = "n/a";
71  }
72 
73  try {
74  auto&& ty = typeAttributes.at(column);
75  switch (ty[0]) {
76  case 's': tuple[column] = symbolTable.unsafeLookup(element); break;
77  case 'i':
78  case 'u':
79  case 'f':
80  case 'r': tuple[column] = RamSignedFromString(element); break;
81  default: fatal("invalid type attribute: `%c`", ty[0]);
82  }
83  } catch (...) {
84  std::stringstream errorMessage;
85  errorMessage << "Error converting number in column " << (column) + 1;
86  throw std::invalid_argument(errorMessage.str());
87  }
88  }
89 
90  return tuple;
91  }
92 
93  void executeSQL(const std::string& sql) {
94  assert(db && "Database connection is closed");
95 
96  char* errorMessage = nullptr;
97  /* Execute SQL statement */
98  int rc = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errorMessage);
99  if (rc != SQLITE_OK) {
100  std::stringstream error;
101  error << "SQLite error in sqlite3_exec: " << sqlite3_errmsg(db) << "\n";
102  error << "SQL error: " << errorMessage << "\n";
103  error << "SQL: " << sql << "\n";
104  sqlite3_free(errorMessage);
105  throw std::invalid_argument(error.str());
106  }
107  }
108 
109  void throwError(const std::string& message) {
110  std::stringstream error;
111  error << message << sqlite3_errmsg(db) << "\n";
112  throw std::invalid_argument(error.str());
113  }
114 
115  void prepareSelectStatement() {
116  std::stringstream selectSQL;
117  selectSQL << "SELECT * FROM '" << relationName << "'";
118  const char* tail = nullptr;
119  if (sqlite3_prepare_v2(db, selectSQL.str().c_str(), -1, &selectStatement, &tail) != SQLITE_OK) {
120  throwError("SQLite error in sqlite3_prepare_v2: ");
121  }
122  }
123 
124  void openDB() {
125  if (sqlite3_open(dbFilename.c_str(), &db) != SQLITE_OK) {
126  throwError("SQLite error in sqlite3_open: ");
127  }
128  sqlite3_extended_result_codes(db, 1);
129  executeSQL("PRAGMA synchronous = OFF");
130  executeSQL("PRAGMA journal_mode = MEMORY");
131  }
132 
133  void checkTableExists() {
134  sqlite3_stmt* tableStatement;
135  std::stringstream selectSQL;
136  selectSQL << "SELECT count(*) FROM sqlite_master WHERE type IN ('table', 'view') AND ";
137  selectSQL << " name = '" << relationName << "';";
138  const char* tail = nullptr;
139 
140  if (sqlite3_prepare_v2(db, selectSQL.str().c_str(), -1, &tableStatement, &tail) != SQLITE_OK) {
141  throwError("SQLite error in sqlite3_prepare_v2: ");
142  }
143 
144  if (sqlite3_step(tableStatement) == SQLITE_ROW) {
145  int count = sqlite3_column_int(tableStatement, 0);
146  if (count > 0) {
147  sqlite3_finalize(tableStatement);
148  return;
149  }
150  }
151  sqlite3_finalize(tableStatement);
152  throw std::invalid_argument(
153  "Required table or view does not exist in " + dbFilename + " for relation " + relationName);
154  }
155 
156  /**
157  * Return given filename or construct from relation name.
158  * Default name is [configured path]/[relation name].sqlite
159  *
160  * @param rwOperation map of IO configuration options
161  * @return input filename
162  */
163  static std::string getFileName(const std::map<std::string, std::string>& rwOperation) {
164  // legacy support for SQLite prior to 2020-03-18
165  // convert dbname to filename
166  auto name = getOr(rwOperation, "dbname", rwOperation.at("name") + ".sqlite");
167  name = getOr(rwOperation, "filename", name);
168 
169  if (name.front() != '/') {
170  name = getOr(rwOperation, "fact-dir", ".") + "/" + name;
171  }
172  return name;
173  }
174 
175  const std::string dbFilename;
176  const std::string relationName;
177  sqlite3_stmt* selectStatement = nullptr;
178  sqlite3* db = nullptr;
179 };
180 
182 public:
183  Own<ReadStream> getReader(const std::map<std::string, std::string>& rwOperation, SymbolTable& symbolTable,
184  RecordTable& recordTable) override {
185  return mk<ReadStreamSQLite>(rwOperation, symbolTable, recordTable);
186  }
187 
188  const std::string& getName() const override {
189  static const std::string name = "sqlite";
190  return name;
191  }
192  ~ReadSQLiteFactory() override = default;
193 };
194 
195 } /* namespace souffle */
souffle::RamSignedFromString
RamSigned RamSignedFromString(const std::string &str, std::size_t *position=nullptr, const int base=10)
Converts a string to a RamSigned.
Definition: StringUtil.h:51
souffle::SerialisationStream< false >::recordTable
RO< RecordTable > & recordTable
Definition: SerialisationStream.h:72
souffle::ReadStreamSQLite::openDB
void openDB()
Definition: ReadStreamSQLite.h:128
souffle::ReadStreamSQLite::readNextTuple
Own< RamDomain[]> readNextTuple() override
Read and return the next tuple.
Definition: ReadStreamSQLite.h:62
SymbolTable.h
souffle::ReadStreamSQLite::executeSQL
void executeSQL(const std::string &sql)
Definition: ReadStreamSQLite.h:97
souffle::RecordTable
Definition: RecordTable.h:114
souffle::Own
std::unique_ptr< A > Own
Definition: ContainerUtil.h:42
MiscUtil.h
souffle::ReadStreamSQLite::checkTableExists
void checkTableExists()
Definition: ReadStreamSQLite.h:137
souffle::ReadStreamSQLite::relationName
const std::string relationName
Definition: ReadStreamSQLite.h:180
souffle::SerialisationStream< false >::symbolTable
RO< SymbolTable > & symbolTable
Definition: SerialisationStream.h:71
souffle::ReadStreamFactory
Definition: ReadStream.h:311
souffle::SerialisationStream< false >::typeAttributes
std::vector< std::string > typeAttributes
Definition: SerialisationStream.h:74
souffle::getOr
C::mapped_type const & getOr(const C &container, typename C::key_type key, const typename C::mapped_type &defaultValue)
Get value for a given key; if not found, return default value.
Definition: ContainerUtil.h:111
souffle::detail::brie::tail
auto tail(C &s)
Definition: Brie.h:110
ReadStream.h
souffle::ReadStreamSQLite::dbFilename
const std::string dbFilename
Definition: ReadStreamSQLite.h:179
souffle::ReadStreamSQLite::~ReadStreamSQLite
~ReadStreamSQLite() override
Definition: ReadStreamSQLite.h:50
StringUtil.h
souffle::SymbolTable
Definition: SymbolTable.h:48
souffle::test::count
int count(const C &c)
Definition: table_test.cpp:40
souffle::ReadStreamSQLite::prepareSelectStatement
void prepareSelectStatement()
Definition: ReadStreamSQLite.h:119
souffle::ReadSQLiteFactory
Definition: ReadStreamSQLite.h:185
souffle::ReadSQLiteFactory::~ReadSQLiteFactory
~ReadSQLiteFactory() override=default
souffle::ReadStreamSQLite::getFileName
static std::string getFileName(const std::map< std::string, std::string > &rwOperation)
Return given filename or construct from relation name.
Definition: ReadStreamSQLite.h:167
souffle::ReadStreamSQLite::ReadStreamSQLite
ReadStreamSQLite(const std::map< std::string, std::string > &rwOperation, SymbolTable &symbolTable, RecordTable &recordTable)
Definition: ReadStreamSQLite.h:41
souffle::ReadStreamSQLite::throwError
void throwError(const std::string &message)
Definition: ReadStreamSQLite.h:113
RamTypes.h
souffle::fatal
void fatal(const char *format, const Args &... args)
Definition: MiscUtil.h:198
souffle
Definition: AggregateOp.h:25
souffle::SerialisationStream< false >::auxiliaryArity
size_t auxiliaryArity
Definition: SerialisationStream.h:77
souffle::SerialisationStream< false >::arity
size_t arity
Definition: SerialisationStream.h:76
souffle::ReadStreamSQLite::selectStatement
sqlite3_stmt * selectStatement
Definition: ReadStreamSQLite.h:181
souffle::ReadSQLiteFactory::getReader
Own< ReadStream > getReader(const std::map< std::string, std::string > &rwOperation, SymbolTable &symbolTable, RecordTable &recordTable) override
Definition: ReadStreamSQLite.h:187
souffle::tuple
Defines a tuple for the OO interface such that relations with varying columns can be accessed.
Definition: SouffleInterface.h:443
souffle::ReadSQLiteFactory::getName
const std::string & getName() const override
Definition: ReadStreamSQLite.h:192
souffle::ReadStreamSQLite::db
sqlite3 * db
Definition: ReadStreamSQLite.h:182
souffle::ReadStream
Definition: ReadStream.h:40