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