1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "td/db/SqliteStatement.h"
8 
9 #include "td/utils/format.h"
10 #include "td/utils/logging.h"
11 #include "td/utils/StackAllocator.h"
12 #include "td/utils/StringBuilder.h"
13 
14 #include "sqlite/sqlite3.h"
15 
16 namespace td {
17 
18 int VERBOSITY_NAME(sqlite) = VERBOSITY_NAME(DEBUG) + 10;
19 
20 namespace {
printExplainQueryPlan(StringBuilder & sb,sqlite3_stmt * pStmt)21 int printExplainQueryPlan(StringBuilder &sb, sqlite3_stmt *pStmt) {
22   const char *zSql = sqlite3_sql(pStmt);
23   if (zSql == nullptr) {
24     return SQLITE_ERROR;
25   }
26 
27   sb << "Explain query " << zSql;
28   char *zExplain = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zSql);
29   if (zExplain == nullptr) {
30     return SQLITE_NOMEM;
31   }
32 
33   sqlite3_stmt *pExplain; /* Compiled EXPLAIN QUERY PLAN command */
34   int rc = sqlite3_prepare_v2(sqlite3_db_handle(pStmt), zExplain, -1, &pExplain, nullptr);
35   sqlite3_free(zExplain);
36   if (rc != SQLITE_OK) {
37     return rc;
38   }
39 
40   while (SQLITE_ROW == sqlite3_step(pExplain)) {
41     int iSelectid = sqlite3_column_int(pExplain, 0);
42     int iOrder = sqlite3_column_int(pExplain, 1);
43     int iFrom = sqlite3_column_int(pExplain, 2);
44     const char *zDetail = reinterpret_cast<const char *>(sqlite3_column_text(pExplain, 3));
45 
46     sb << '\n' << iSelectid << ' ' << iOrder << ' ' << iFrom << ' ' << zDetail;
47   }
48 
49   return sqlite3_finalize(pExplain);
50 }
51 }  // namespace
52 
SqliteStatement(sqlite3_stmt * stmt,std::shared_ptr<detail::RawSqliteDb> db)53 SqliteStatement::SqliteStatement(sqlite3_stmt *stmt, std::shared_ptr<detail::RawSqliteDb> db)
54     : stmt_(stmt), db_(std::move(db)) {
55   CHECK(stmt != nullptr);
56 }
57 SqliteStatement::~SqliteStatement() = default;
58 
explain()59 Result<string> SqliteStatement::explain() {
60   if (empty()) {
61     return Status::Error("No statement");
62   }
63   auto tmp = StackAllocator::alloc(10000);
64   StringBuilder sb(tmp.as_slice());
65   auto code = printExplainQueryPlan(sb, stmt_.get());
66   if (code != SQLITE_OK) {
67     return last_error();
68   }
69   if (sb.is_error()) {
70     return Status::Error("StringBuilder buffer overflow");
71   }
72   return sb.as_cslice().str();
73 }
bind_blob(int id,Slice blob)74 Status SqliteStatement::bind_blob(int id, Slice blob) {
75   auto rc = sqlite3_bind_blob(stmt_.get(), id, blob.data(), static_cast<int>(blob.size()), nullptr);
76   if (rc != SQLITE_OK) {
77     return last_error();
78   }
79   return Status::OK();
80 }
bind_string(int id,Slice str)81 Status SqliteStatement::bind_string(int id, Slice str) {
82   auto rc = sqlite3_bind_text(stmt_.get(), id, str.data(), static_cast<int>(str.size()), nullptr);
83   if (rc != SQLITE_OK) {
84     return last_error();
85   }
86   return Status::OK();
87 }
88 
bind_int32(int id,int32 value)89 Status SqliteStatement::bind_int32(int id, int32 value) {
90   auto rc = sqlite3_bind_int(stmt_.get(), id, value);
91   if (rc != SQLITE_OK) {
92     return last_error();
93   }
94   return Status::OK();
95 }
bind_int64(int id,int64 value)96 Status SqliteStatement::bind_int64(int id, int64 value) {
97   auto rc = sqlite3_bind_int64(stmt_.get(), id, value);
98   if (rc != SQLITE_OK) {
99     return last_error();
100   }
101   return Status::OK();
102 }
bind_null(int id)103 Status SqliteStatement::bind_null(int id) {
104   auto rc = sqlite3_bind_null(stmt_.get(), id);
105   if (rc != SQLITE_OK) {
106     return last_error();
107   }
108   return Status::OK();
109 }
110 
operator <<(StringBuilder & sb,SqliteStatement::Datatype type)111 StringBuilder &operator<<(StringBuilder &sb, SqliteStatement::Datatype type) {
112   using Datatype = SqliteStatement::Datatype;
113   switch (type) {
114     case Datatype::Integer:
115       return sb << "Integer";
116     case Datatype::Float:
117       return sb << "Float";
118     case Datatype::Blob:
119       return sb << "Blob";
120     case Datatype::Null:
121       return sb << "Null";
122     case Datatype::Text:
123       return sb << "Text";
124   }
125   UNREACHABLE();
126   return sb;
127 }
view_blob(int id)128 Slice SqliteStatement::view_blob(int id) {
129   LOG_IF(ERROR, view_datatype(id) != Datatype::Blob) << view_datatype(id);
130   auto *data = sqlite3_column_blob(stmt_.get(), id);
131   auto size = sqlite3_column_bytes(stmt_.get(), id);
132   if (data == nullptr) {
133     return Slice();
134   }
135   return Slice(static_cast<const char *>(data), size);
136 }
view_string(int id)137 Slice SqliteStatement::view_string(int id) {
138   LOG_IF(ERROR, view_datatype(id) != Datatype::Text) << view_datatype(id);
139   auto *data = sqlite3_column_text(stmt_.get(), id);
140   auto size = sqlite3_column_bytes(stmt_.get(), id);
141   if (data == nullptr) {
142     return Slice();
143   }
144   return Slice(data, size);
145 }
view_int32(int id)146 int32 SqliteStatement::view_int32(int id) {
147   LOG_IF(ERROR, view_datatype(id) != Datatype::Integer) << view_datatype(id);
148   return sqlite3_column_int(stmt_.get(), id);
149 }
view_int64(int id)150 int64 SqliteStatement::view_int64(int id) {
151   LOG_IF(ERROR, view_datatype(id) != Datatype::Integer) << view_datatype(id);
152   return sqlite3_column_int64(stmt_.get(), id);
153 }
view_datatype(int id)154 SqliteStatement::Datatype SqliteStatement::view_datatype(int id) {
155   auto type = sqlite3_column_type(stmt_.get(), id);
156   switch (type) {
157     case SQLITE_INTEGER:
158       return Datatype::Integer;
159     case SQLITE_FLOAT:
160       return Datatype::Float;
161     case SQLITE_BLOB:
162       return Datatype::Blob;
163     case SQLITE_NULL:
164       return Datatype::Null;
165     case SQLITE3_TEXT:
166       return Datatype::Text;
167     default:
168       UNREACHABLE();
169   }
170 }
171 
reset()172 void SqliteStatement::reset() {
173   sqlite3_reset(stmt_.get());
174   state_ = State::Start;
175 }
176 
step()177 Status SqliteStatement::step() {
178   if (state_ == State::Finish) {
179     return Status::Error("One has to reset statement");
180   }
181   VLOG(sqlite) << "Start step " << tag("query", sqlite3_sql(stmt_.get())) << tag("statement", stmt_.get())
182                << tag("database", db_.get());
183   auto rc = sqlite3_step(stmt_.get());
184   VLOG(sqlite) << "Finish step " << tag("query", sqlite3_sql(stmt_.get())) << tag("statement", stmt_.get())
185                << tag("database", db_.get());
186   if (rc == SQLITE_ROW) {
187     state_ = State::GotRow;
188     return Status::OK();
189   }
190 
191   state_ = State::Finish;
192   if (rc == SQLITE_DONE) {
193     return Status::OK();
194   }
195   return last_error();
196 }
197 
operator ()(sqlite3_stmt * stmt)198 void SqliteStatement::StmtDeleter::operator()(sqlite3_stmt *stmt) {
199   sqlite3_finalize(stmt);
200 }
201 
last_error()202 Status SqliteStatement::last_error() {
203   return db_->last_error();
204 }
205 
206 }  // namespace td
207