36 |
36 |
37 #include <relpipe/writer/Factory.h> |
37 #include <relpipe/writer/Factory.h> |
38 |
38 |
39 #include "Configuration.h" |
39 #include "Configuration.h" |
40 #include "SqlException.h" |
40 #include "SqlException.h" |
|
41 #include "PreparedStatement.h" |
41 |
42 |
42 namespace relpipe { |
43 namespace relpipe { |
43 namespace tr { |
44 namespace tr { |
44 namespace sql { |
45 namespace sql { |
45 |
46 |
46 using namespace std; |
47 using namespace std; |
47 using namespace relpipe; |
48 using namespace relpipe; |
48 using namespace relpipe::reader; |
49 using namespace relpipe::reader; |
49 using namespace relpipe::reader::handlers; |
50 using namespace relpipe::reader::handlers; |
50 |
|
51 class PreparedStatement { |
|
52 private: |
|
53 sqlite3_stmt* stmt; |
|
54 |
|
55 public: |
|
56 |
|
57 PreparedStatement(sqlite3_stmt* stmt) : stmt(stmt) { |
|
58 } |
|
59 |
|
60 virtual ~PreparedStatement() { |
|
61 sqlite3_finalize(stmt); |
|
62 } |
|
63 |
|
64 void setBoolean(int parameterIndex, relpipe::reader::boolean_t value) { |
|
65 int result = sqlite3_bind_int(stmt, parameterIndex, value); |
|
66 if (result != SQLITE_OK) throw SqlException(L"Unable to set SQLite parameter."); |
|
67 } |
|
68 |
|
69 void setInteger(int parameterIndex, relpipe::reader::integer_t value) { |
|
70 int result = sqlite3_bind_int64(stmt, parameterIndex, value); |
|
71 if (result != SQLITE_OK) throw SqlException(L"Unable to set SQLite parameter."); |
|
72 } |
|
73 |
|
74 void setString(int parameterIndex, std::string value) { |
|
75 int result = sqlite3_bind_text(stmt, parameterIndex, value.c_str(), -1, SQLITE_TRANSIENT); |
|
76 if (result != SQLITE_OK) throw SqlException(L"Unable to set SQLite parameter."); |
|
77 } |
|
78 |
|
79 void setNull(int parameterIndex) { |
|
80 int result = sqlite3_bind_null(stmt, parameterIndex); |
|
81 if (result != SQLITE_OK) throw SqlException(L"Unable to set SQLite parameter."); |
|
82 } |
|
83 |
|
84 bool next() { |
|
85 int result = sqlite3_step(stmt); |
|
86 if (result == SQLITE_ROW) return true; |
|
87 else if (result == SQLITE_DONE) return false; |
|
88 else throw SqlException(L"Error while iterating over SQLite result."); |
|
89 } |
|
90 |
|
91 void reset() { |
|
92 int result = sqlite3_reset(stmt); |
|
93 if (result != SQLITE_OK) throw SqlException(L"Unable to reset SQLite prepared statement."); |
|
94 } |
|
95 |
|
96 int getColumnCount() { |
|
97 return sqlite3_column_count(stmt); |
|
98 } |
|
99 |
|
100 std::string getColumName(int columnIndex) { |
|
101 const char* name = sqlite3_column_name(stmt, columnIndex); |
|
102 if (name) return name; |
|
103 else throw SqlException(L"Unable to get SQLite column name."); |
|
104 } |
|
105 |
|
106 relpipe::writer::TypeId getColumType(int columnIndex, relpipe::writer::TypeId defaultType = relpipe::writer::TypeId::STRING) { |
|
107 const char* type = sqlite3_column_decltype(stmt, columnIndex); |
|
108 |
|
109 // TODO: sqlite3_column_decltype returns value only for columns of existing tables, not for dynamic expressions – SQLite uses dynamic types |
|
110 // maybe we could write a function/module that returns result set metadata for given query (before executing it) |
|
111 // or use at least explicit casts in SQL and modify sqlite3_column_decltype() function or add some new one to return such casted type |
|
112 // |
|
113 // fprintf(stderr, "%d → %s\n", columnIndex, type); |
|
114 // SELECT typeof(1+1); == "integer" |
|
115 // https://www.sqlite.org/c3ref/column_decltype.html – sqlite3_column_decltype |
|
116 // https://www.sqlite.org/c3ref/column_blob.html – sqlite3_column_type |
|
117 // https://www.sqlite.org/datatype3.html – Datatypes In SQLite Version 3 |
|
118 // https://dba.stackexchange.com/questions/203220/sqlite-what-is-the-use-of-specifying-data-types |
|
119 // https://www.mail-archive.com/sqlite-users@mailinglists.sqlite.org/msg118093.html |
|
120 |
|
121 if (type == nullptr) return relpipe::writer::TypeId::STRING; |
|
122 else if (strcmp(type, "integer") == 0) return relpipe::writer::TypeId::INTEGER; |
|
123 else if (strcmp(type, "text") == 0) return relpipe::writer::TypeId::STRING; |
|
124 else return defaultType; |
|
125 // TODO: support also other data types |
|
126 } |
|
127 |
|
128 std::string getString(int columnIndex) { |
|
129 const char* value = (const char*) sqlite3_column_text(stmt, columnIndex); |
|
130 return value ? value : ""; // TODO: support NULL values (when supported in relpipe format) |
|
131 } |
|
132 |
|
133 }; |
|
134 |
51 |
135 class Connection { |
52 class Connection { |
136 private: |
53 private: |
137 sqlite3* db; |
54 sqlite3* db; |
138 |
55 |