souffle  2.0.2-371-g6315b36
WriteStreamSQLite.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 WriteStreamSQLite.h
12  *
13  ***********************************************************************/
14 
15 #pragma once
16 
17 #include "souffle/RamTypes.h"
18 #include "souffle/SymbolTable.h"
19 #include "souffle/io/WriteStream.h"
20 #include <cassert>
21 #include <cstddef>
22 #include <cstdint>
23 #include <map>
24 #include <memory>
25 #include <sstream>
26 #include <stdexcept>
27 #include <string>
28 #include <unordered_map>
29 #include <vector>
30 #include <sqlite3.h>
31 
32 namespace souffle {
33 
34 class RecordTable;
35 
36 class WriteStreamSQLite : public WriteStream {
37 public:
38  WriteStreamSQLite(const std::map<std::string, std::string>& rwOperation, const SymbolTable& symbolTable,
39  const RecordTable& recordTable)
40  : WriteStream(rwOperation, symbolTable, recordTable), dbFilename(getFileName(rwOperation)),
41  relationName(rwOperation.at("name")) {
42  openDB();
43  createTables();
45  // executeSQL("BEGIN TRANSACTION", db);
46  }
47 
48  ~WriteStreamSQLite() override {
49  sqlite3_finalize(insertStatement);
50  sqlite3_finalize(symbolInsertStatement);
51  sqlite3_finalize(symbolSelectStatement);
52  sqlite3_close(db);
53  }
54 
55 protected:
56  void writeNullary() override {}
57 
58  void writeNextTuple(const RamDomain* tuple) override {
59  for (size_t i = 0; i < arity; i++) {
60  RamDomain value = 0; // Silence warning
61 
62  switch (typeAttributes.at(i)[0]) {
63  case 's': value = getSymbolTableID(tuple[i]); break;
64  default: value = tuple[i]; break;
65  }
66 
67 #if RAM_DOMAIN_SIZE == 64
68  if (sqlite3_bind_int64(insertStatement, i + 1, value) != SQLITE_OK) {
69 #else
70  if (sqlite3_bind_int(insertStatement, i + 1, value) != SQLITE_OK) {
71 #endif
72  throwError("SQLite error in sqlite3_bind_text: ");
73  }
74  }
75  if (sqlite3_step(insertStatement) != SQLITE_DONE) {
76  throwError("SQLite error in sqlite3_step: ");
77  }
78  sqlite3_clear_bindings(insertStatement);
79  sqlite3_reset(insertStatement);
80  }
81 
82 private:
83  void executeSQL(const std::string& sql, sqlite3* db) {
84  assert(db && "Database connection is closed");
85 
86  char* errorMessage = nullptr;
87  /* Execute SQL statement */
88  int rc = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &errorMessage);
89  if (rc != SQLITE_OK) {
90  std::stringstream error;
91  error << "SQLite error in sqlite3_exec: " << sqlite3_errmsg(db) << "\n";
92  error << "SQL error: " << errorMessage << "\n";
93  error << "SQL: " << sql << "\n";
94  sqlite3_free(errorMessage);
95  throw std::invalid_argument(error.str());
96  }
97  }
98 
99  void throwError(const std::string& message) {
100  std::stringstream error;
101  error << message << sqlite3_errmsg(db) << "\n";
102  throw std::invalid_argument(error.str());
103  }
104 
105  uint64_t getSymbolTableIDFromDB(int index) {
106  if (sqlite3_bind_text(symbolSelectStatement, 1, symbolTable.unsafeResolve(index).c_str(), -1,
107  SQLITE_TRANSIENT) != SQLITE_OK) {
108  throwError("SQLite error in sqlite3_bind_text: ");
109  }
110  if (sqlite3_step(symbolSelectStatement) != SQLITE_ROW) {
111  throwError("SQLite error in sqlite3_step: ");
112  }
113  uint64_t rowid = sqlite3_column_int64(symbolSelectStatement, 0);
114  sqlite3_clear_bindings(symbolSelectStatement);
115  sqlite3_reset(symbolSelectStatement);
116  return rowid;
117  }
118  uint64_t getSymbolTableID(int index) {
119  if (dbSymbolTable.count(index) != 0) {
120  return dbSymbolTable[index];
121  }
122 
123  if (sqlite3_bind_text(symbolInsertStatement, 1, symbolTable.unsafeResolve(index).c_str(), -1,
124  SQLITE_TRANSIENT) != SQLITE_OK) {
125  throwError("SQLite error in sqlite3_bind_text: ");
126  }
127  // Either the insert succeeds and we have a new row id or it already exists and a select is needed.
128  uint64_t rowid;
129  if (sqlite3_step(symbolInsertStatement) != SQLITE_DONE) {
130  // The symbol already exists so select it.
131  rowid = getSymbolTableIDFromDB(index);
132  } else {
133  rowid = sqlite3_last_insert_rowid(db);
134  }
135  sqlite3_clear_bindings(symbolInsertStatement);
136  sqlite3_reset(symbolInsertStatement);
137 
138  dbSymbolTable[index] = rowid;
139  return rowid;
140  }
141 
142  void openDB() {
143  if (sqlite3_open(dbFilename.c_str(), &db) != SQLITE_OK) {
144  throwError("SQLite error in sqlite3_open");
145  }
146  sqlite3_extended_result_codes(db, 1);
147  executeSQL("PRAGMA synchronous = OFF", db);
148  executeSQL("PRAGMA journal_mode = MEMORY", db);
149  }
150 
151  void prepareStatements() {
155  }
157  std::stringstream insertSQL;
158  insertSQL << "INSERT INTO " << symbolTableName;
159  insertSQL << " VALUES(null,@V0);";
160  const char* tail = nullptr;
161  if (sqlite3_prepare_v2(db, insertSQL.str().c_str(), -1, &symbolInsertStatement, &tail) != SQLITE_OK) {
162  throwError("SQLite error in sqlite3_prepare_v2: ");
163  }
164  }
165 
167  std::stringstream selectSQL;
168  selectSQL << "SELECT id FROM " << symbolTableName;
169  selectSQL << " WHERE symbol = @V0;";
170  const char* tail = nullptr;
171  if (sqlite3_prepare_v2(db, selectSQL.str().c_str(), -1, &symbolSelectStatement, &tail) != SQLITE_OK) {
172  throwError("SQLite error in sqlite3_prepare_v2: ");
173  }
174  }
175 
176  void prepareInsertStatement() {
177  std::stringstream insertSQL;
178  insertSQL << "INSERT INTO '_" << relationName << "' VALUES ";
179  insertSQL << "(@V0";
180  for (unsigned int i = 1; i < arity; i++) {
181  insertSQL << ",@V" << i;
182  }
183  insertSQL << ");";
184  const char* tail = nullptr;
185  if (sqlite3_prepare_v2(db, insertSQL.str().c_str(), -1, &insertStatement, &tail) != SQLITE_OK) {
186  throwError("SQLite error in sqlite3_prepare_v2: ");
187  }
188  }
189 
190  void createTables() {
194  }
195 
196  void createRelationTable() {
197  std::stringstream createTableText;
198  createTableText << "CREATE TABLE IF NOT EXISTS '_" << relationName << "' (";
199  if (arity > 0) {
200  createTableText << "'0' INTEGER";
201  for (unsigned int i = 1; i < arity; i++) {
202  createTableText << ",'" << std::to_string(i) << "' ";
203  createTableText << "INTEGER";
204  }
205  }
206  createTableText << ");";
207  executeSQL(createTableText.str(), db);
208  executeSQL("DELETE FROM '_" + relationName + "';", db);
209  }
210 
211  void createRelationView() {
212  // Create view with symbol strings resolved
213  std::stringstream createViewText;
214  createViewText << "CREATE VIEW IF NOT EXISTS '" << relationName << "' AS ";
215  std::stringstream projectionClause;
216  std::stringstream fromClause;
217  fromClause << "'_" << relationName << "'";
218  std::stringstream whereClause;
219  bool firstWhere = true;
220  for (unsigned int i = 0; i < arity; i++) {
221  std::string columnName = std::to_string(i);
222  if (i != 0) {
223  projectionClause << ",";
224  }
225  if (typeAttributes.at(i)[0] == 's') {
226  projectionClause << "'_symtab_" << columnName << "'.symbol AS '" << columnName << "'";
227  fromClause << ",'" << symbolTableName << "' AS '_symtab_" << columnName << "'";
228  if (!firstWhere) {
229  whereClause << " AND ";
230  } else {
231  firstWhere = false;
232  }
233  whereClause << "'_" << relationName << "'.'" << columnName << "' = "
234  << "'_symtab_" << columnName << "'.id";
235  } else {
236  projectionClause << "'_" << relationName << "'.'" << columnName << "'";
237  }
238  }
239  createViewText << "SELECT " << projectionClause.str() << " FROM " << fromClause.str();
240  if (!firstWhere) {
241  createViewText << " WHERE " << whereClause.str();
242  }
243  createViewText << ";";
244  executeSQL(createViewText.str(), db);
245  }
246  void createSymbolTable() {
247  std::stringstream createTableText;
248  createTableText << "CREATE TABLE IF NOT EXISTS '" << symbolTableName << "' ";
249  createTableText << "(id INTEGER PRIMARY KEY, symbol TEXT UNIQUE);";
250  executeSQL(createTableText.str(), db);
251  }
252 
253  /**
254  * Return given filename or construct from relation name.
255  * Default name is [configured path]/[relation name].sqlite
256  *
257  * @param rwOperation map of IO configuration options
258  * @return input filename
259  */
260  static std::string getFileName(const std::map<std::string, std::string>& rwOperation) {
261  // legacy support for SQLite prior to 2020-03-18
262  // convert dbname to filename
263  auto name = getOr(rwOperation, "dbname", rwOperation.at("name") + ".sqlite");
264  name = getOr(rwOperation, "filename", name);
265 
266  if (name.front() != '/') {
267  name = getOr(rwOperation, "output-dir", ".") + "/" + name;
268  }
269  return name;
270  }
271 
272  const std::string dbFilename;
273  const std::string relationName;
274  const std::string symbolTableName = "__SymbolTable";
275 
276  std::unordered_map<uint64_t, uint64_t> dbSymbolTable;
277  sqlite3_stmt* insertStatement = nullptr;
278  sqlite3_stmt* symbolInsertStatement = nullptr;
279  sqlite3_stmt* symbolSelectStatement = nullptr;
280  sqlite3* db = nullptr;
281 };
282 
284 public:
285  Own<WriteStream> getWriter(const std::map<std::string, std::string>& rwOperation,
286  const SymbolTable& symbolTable, const RecordTable& recordTable) override {
287  return mk<WriteStreamSQLite>(rwOperation, symbolTable, recordTable);
288  }
289 
290  const std::string& getName() const override {
291  static const std::string name = "sqlite";
292  return name;
293  }
294  ~WriteSQLiteFactory() override = default;
295 };
296 
297 } /* namespace souffle */
souffle::WriteStreamSQLite::getSymbolTableID
uint64_t getSymbolTableID(int index)
Definition: WriteStreamSQLite.h:122
souffle::WriteStreamSQLite::createTables
void createTables()
Definition: WriteStreamSQLite.h:194
souffle::SerialisationStream< true >::recordTable
RO< RecordTable > & recordTable
Definition: SerialisationStream.h:72
souffle::WriteStreamSQLite::openDB
void openDB()
Definition: WriteStreamSQLite.h:146
souffle::WriteStreamSQLite::~WriteStreamSQLite
~WriteStreamSQLite() override
Definition: WriteStreamSQLite.h:52
souffle::WriteStreamSQLite::insertStatement
sqlite3_stmt * insertStatement
Definition: WriteStreamSQLite.h:281
souffle::WriteStreamSQLite::db
sqlite3 * db
Definition: WriteStreamSQLite.h:284
souffle::RamDomain
int32_t RamDomain
Definition: RamTypes.h:56
souffle::WriteStreamSQLite::writeNextTuple
void writeNextTuple(const RamDomain *tuple) override
Definition: WriteStreamSQLite.h:62
SymbolTable.h
souffle::RecordTable
Definition: RecordTable.h:114
souffle::WriteStreamSQLite::dbFilename
const std::string dbFilename
Definition: WriteStreamSQLite.h:276
souffle::WriteStreamSQLite::prepareSymbolSelectStatement
void prepareSymbolSelectStatement()
Definition: WriteStreamSQLite.h:170
souffle::Own
std::unique_ptr< A > Own
Definition: ContainerUtil.h:42
souffle::WriteStream
Definition: WriteStream.h:38
souffle::WriteStreamSQLite::writeNullary
void writeNullary() override
Definition: WriteStreamSQLite.h:60
souffle::WriteStreamSQLite::getFileName
static std::string getFileName(const std::map< std::string, std::string > &rwOperation)
Return given filename or construct from relation name.
Definition: WriteStreamSQLite.h:264
souffle::SerialisationStream< true >::symbolTable
RO< SymbolTable > & symbolTable
Definition: SerialisationStream.h:71
souffle::SerialisationStream< true >::typeAttributes
std::vector< std::string > typeAttributes
Definition: SerialisationStream.h:74
souffle::WriteStreamSQLite::getSymbolTableIDFromDB
uint64_t getSymbolTableIDFromDB(int index)
Definition: WriteStreamSQLite.h:109
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
i
size_t i
Definition: json11.h:663
souffle::WriteStreamSQLite::relationName
const std::string relationName
Definition: WriteStreamSQLite.h:277
souffle::WriteStreamSQLite::createRelationTable
void createRelationTable()
Definition: WriteStreamSQLite.h:200
souffle::WriteStreamSQLite::createRelationView
void createRelationView()
Definition: WriteStreamSQLite.h:215
souffle::WriteSQLiteFactory
Definition: WriteStreamSQLite.h:287
souffle::WriteStreamSQLite::dbSymbolTable
std::unordered_map< uint64_t, uint64_t > dbSymbolTable
Definition: WriteStreamSQLite.h:280
souffle::WriteStreamSQLite::prepareInsertStatement
void prepareInsertStatement()
Definition: WriteStreamSQLite.h:180
souffle::SymbolTable
Definition: SymbolTable.h:48
souffle::WriteStreamSQLite::prepareStatements
void prepareStatements()
Definition: WriteStreamSQLite.h:155
souffle::WriteStreamSQLite::createSymbolTable
void createSymbolTable()
Definition: WriteStreamSQLite.h:250
souffle::WriteStreamSQLite::symbolTableName
const std::string symbolTableName
Definition: WriteStreamSQLite.h:278
WriteStream.h
souffle::WriteStreamSQLite::WriteStreamSQLite
WriteStreamSQLite(const std::map< std::string, std::string > &rwOperation, const SymbolTable &symbolTable, const RecordTable &recordTable)
Definition: WriteStreamSQLite.h:42
RamTypes.h
souffle::WriteStreamSQLite::prepareSymbolInsertStatement
void prepareSymbolInsertStatement()
Definition: WriteStreamSQLite.h:160
souffle::WriteStreamSQLite::throwError
void throwError(const std::string &message)
Definition: WriteStreamSQLite.h:103
souffle
Definition: AggregateOp.h:25
souffle::SerialisationStream< true >::arity
size_t arity
Definition: SerialisationStream.h:76
souffle::WriteStreamSQLite::executeSQL
void executeSQL(const std::string &sql, sqlite3 *db)
Definition: WriteStreamSQLite.h:87
souffle::WriteStreamFactory
Definition: WriteStream.h:193
souffle::tuple
Defines a tuple for the OO interface such that relations with varying columns can be accessed.
Definition: SouffleInterface.h:443
souffle::WriteStreamSQLite::symbolInsertStatement
sqlite3_stmt * symbolInsertStatement
Definition: WriteStreamSQLite.h:282
souffle::WriteStreamSQLite::symbolSelectStatement
sqlite3_stmt * symbolSelectStatement
Definition: WriteStreamSQLite.h:283