|
1 /** |
|
2 * Relational pipes |
|
3 * Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info) |
|
4 * |
|
5 * This program is free software: you can redistribute it and/or modify |
|
6 * it under the terms of the GNU General Public License as published by |
|
7 * the Free Software Foundation, version 3. |
|
8 * |
|
9 * This program is distributed in the hope that it will be useful, |
|
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 * GNU General Public License for more details. |
|
13 * |
|
14 * You should have received a copy of the GNU General Public License |
|
15 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 */ |
|
17 |
|
18 #include "PreparedStatement.h" |
|
19 |
|
20 namespace relpipe { |
|
21 namespace tr { |
|
22 namespace sql { |
|
23 |
|
24 PreparedStatement::PreparedStatement(sqlite3_stmt* stmt) : stmt(stmt) { |
|
25 } |
|
26 |
|
27 PreparedStatement::~PreparedStatement() { |
|
28 sqlite3_finalize(stmt); |
|
29 } |
|
30 |
|
31 void PreparedStatement::setBoolean(int parameterIndex, relpipe::reader::boolean_t value) { |
|
32 int result = sqlite3_bind_int(stmt, parameterIndex, value); |
|
33 if (result != SQLITE_OK) throw SqlException(L"Unable to set SQLite parameter."); |
|
34 } |
|
35 |
|
36 void PreparedStatement::setInteger(int parameterIndex, relpipe::reader::integer_t value) { |
|
37 int result = sqlite3_bind_int64(stmt, parameterIndex, value); |
|
38 if (result != SQLITE_OK) throw SqlException(L"Unable to set SQLite parameter."); |
|
39 } |
|
40 |
|
41 void PreparedStatement::setString(int parameterIndex, std::string value) { |
|
42 int result = sqlite3_bind_text(stmt, parameterIndex, value.c_str(), -1, SQLITE_TRANSIENT); |
|
43 if (result != SQLITE_OK) throw SqlException(L"Unable to set SQLite parameter."); |
|
44 } |
|
45 |
|
46 void PreparedStatement::setNull(int parameterIndex) { |
|
47 int result = sqlite3_bind_null(stmt, parameterIndex); |
|
48 if (result != SQLITE_OK) throw SqlException(L"Unable to set SQLite parameter."); |
|
49 } |
|
50 |
|
51 bool PreparedStatement::next() { |
|
52 int result = sqlite3_step(stmt); |
|
53 if (result == SQLITE_ROW) return true; |
|
54 else if (result == SQLITE_DONE) return false; |
|
55 else throw SqlException(L"Error while iterating over SQLite result."); |
|
56 } |
|
57 |
|
58 void PreparedStatement::reset() { |
|
59 int result = sqlite3_reset(stmt); |
|
60 if (result != SQLITE_OK) throw SqlException(L"Unable to reset SQLite prepared statement."); |
|
61 } |
|
62 |
|
63 int PreparedStatement::getColumnCount() { |
|
64 return sqlite3_column_count(stmt); |
|
65 } |
|
66 |
|
67 std::string PreparedStatement::getColumName(int columnIndex) { |
|
68 const char* name = sqlite3_column_name(stmt, columnIndex); |
|
69 if (name) return name; |
|
70 else throw SqlException(L"Unable to get SQLite column name."); |
|
71 } |
|
72 |
|
73 relpipe::writer::TypeId PreparedStatement::getColumType(int columnIndex, relpipe::writer::TypeId defaultType) { |
|
74 const char* type = sqlite3_column_decltype(stmt, columnIndex); |
|
75 |
|
76 // TODO: sqlite3_column_decltype returns value only for columns of existing tables, not for dynamic expressions – SQLite uses dynamic types |
|
77 // maybe we could write a function/module that returns result set metadata for given query (before executing it) |
|
78 // or use at least explicit casts in SQL and modify sqlite3_column_decltype() function or add some new one to return such casted type |
|
79 // |
|
80 // fprintf(stderr, "%d → %s\n", columnIndex, type); |
|
81 // SELECT typeof(1+1); == "integer" |
|
82 // https://www.sqlite.org/c3ref/column_decltype.html – sqlite3_column_decltype |
|
83 // https://www.sqlite.org/c3ref/column_blob.html – sqlite3_column_type |
|
84 // https://www.sqlite.org/datatype3.html – Datatypes In SQLite Version 3 |
|
85 // https://dba.stackexchange.com/questions/203220/sqlite-what-is-the-use-of-specifying-data-types |
|
86 // https://www.mail-archive.com/sqlite-users@mailinglists.sqlite.org/msg118093.html |
|
87 |
|
88 if (type == nullptr) return relpipe::writer::TypeId::STRING; |
|
89 else if (strcmp(type, "integer") == 0) return relpipe::writer::TypeId::INTEGER; |
|
90 else if (strcmp(type, "text") == 0) return relpipe::writer::TypeId::STRING; |
|
91 else return defaultType; |
|
92 // TODO: support also other data types |
|
93 } |
|
94 |
|
95 std::string PreparedStatement::getString(int columnIndex) { |
|
96 const char* value = (const char*) sqlite3_column_text(stmt, columnIndex); |
|
97 return value ? value : ""; // TODO: support NULL values (when supported in relpipe format) |
|
98 } |
|
99 |
|
100 } |
|
101 } |
|
102 } |