101 const char* name = sqlite3_column_name(stmt, columnIndex); |
101 const char* name = sqlite3_column_name(stmt, columnIndex); |
102 if (name) return name; |
102 if (name) return name; |
103 else throw SqlException(L"Unable to get SQLite column name."); |
103 else throw SqlException(L"Unable to get SQLite column name."); |
104 } |
104 } |
105 |
105 |
106 relpipe::writer::TypeId getColumType(int columnIndex) { |
106 relpipe::writer::TypeId getColumType(int columnIndex, relpipe::writer::TypeId defaultType = relpipe::writer::TypeId::STRING) { |
107 const char* type = sqlite3_column_decltype(stmt, columnIndex); |
107 const char* type = sqlite3_column_decltype(stmt, columnIndex); |
108 |
108 |
109 // TODO: sqlite3_column_decltype returns value only for columns of existing tables, not for dynamic expressions – SQLite uses dynamic types |
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 // |
110 // fprintf(stderr, "%d → %s\n", columnIndex, type); |
113 // fprintf(stderr, "%d → %s\n", columnIndex, type); |
111 // SELECT typeof(1+1); == "integer" |
114 // SELECT typeof(1+1); == "integer" |
112 // https://www.sqlite.org/c3ref/column_decltype.html – sqlite3_column_decltype |
115 // https://www.sqlite.org/c3ref/column_decltype.html – sqlite3_column_decltype |
113 // https://www.sqlite.org/c3ref/column_blob.html – sqlite3_column_type |
116 // https://www.sqlite.org/c3ref/column_blob.html – sqlite3_column_type |
114 // https://www.sqlite.org/datatype3.html – Datatypes In SQLite Version 3 |
117 // https://www.sqlite.org/datatype3.html – Datatypes In SQLite Version 3 |
115 // https://dba.stackexchange.com/questions/203220/sqlite-what-is-the-use-of-specifying-data-types |
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 |
116 |
120 |
117 if (type == nullptr) return relpipe::writer::TypeId::STRING; |
121 if (type == nullptr) return relpipe::writer::TypeId::STRING; |
118 else if (strcmp(type, "integer") == 0) return relpipe::writer::TypeId::INTEGER; |
122 else if (strcmp(type, "integer") == 0) return relpipe::writer::TypeId::INTEGER; |
119 else if (strcmp(type, "text") == 0) return relpipe::writer::TypeId::STRING; |
123 else if (strcmp(type, "text") == 0) return relpipe::writer::TypeId::STRING; |
120 else return relpipe::writer::TypeId::STRING; |
124 else return defaultType; |
121 // TODO: support also other data types |
125 // TODO: support also other data types |
122 } |
126 } |
123 |
127 |
124 std::string getString(int columnIndex) { |
128 std::string getString(int columnIndex) { |
125 const char* value = (const char*) sqlite3_column_text(stmt, columnIndex); |
129 const char* value = (const char*) sqlite3_column_text(stmt, columnIndex); |
200 std::unique_ptr<PreparedStatement> prepared(connection->prepareStatement(convertor.to_bytes(sql.str()).c_str())); |
204 std::unique_ptr<PreparedStatement> prepared(connection->prepareStatement(convertor.to_bytes(sql.str()).c_str())); |
201 while (prepared->next()); |
205 while (prepared->next()); |
202 } |
206 } |
203 } |
207 } |
204 |
208 |
|
209 relpipe::writer::TypeId findType(string_t columnName, int columnIndex, const Statement& statement, std::shared_ptr<PreparedStatement> preparedStatement) { |
|
210 for (TypeCast typeCast : statement.typeCasts) if (typeCast.name == columnName) return relationalWriter->toTypeId(typeCast.type); |
|
211 return preparedStatement->getColumType(columnIndex); |
|
212 } |
|
213 |
205 void processStatement(const Statement& statement) { |
214 void processStatement(const Statement& statement) { |
206 std::unique_ptr<PreparedStatement> prepared(connection->prepareStatement(convertor.to_bytes(statement.sql).c_str())); |
215 std::shared_ptr<PreparedStatement> prepared(connection->prepareStatement(convertor.to_bytes(statement.sql).c_str())); |
207 int columnCount = prepared->getColumnCount(); |
216 int columnCount = prepared->getColumnCount(); |
208 int parameterCount = statement.parameters.size(); |
217 int parameterCount = statement.parameters.size(); |
209 |
218 |
210 for (int i = 0; i < parameterCount; i++) { |
219 for (int i = 0; i < parameterCount; i++) { |
211 prepared->setString(i + 1, convertor.to_bytes(statement.parameters[i].value)); |
220 prepared->setString(i + 1, convertor.to_bytes(statement.parameters[i].value)); |
212 } |
221 } |
213 |
222 |
214 std::vector<relpipe::writer::AttributeMetadata> metadata; |
223 std::vector<relpipe::writer::AttributeMetadata> metadata; |
215 for (int i = 0; i < columnCount; i++) metadata.push_back({convertor.from_bytes(prepared->getColumName(i).c_str()), prepared->getColumType(i)}); |
224 for (int i = 0; i < columnCount; i++) { |
|
225 string_t columnName = convertor.from_bytes(prepared->getColumName(i).c_str()); |
|
226 metadata.push_back({columnName, findType(columnName, i, statement, prepared)}); |
|
227 } |
216 relationalWriter->startRelation(statement.relation, metadata, true); |
228 relationalWriter->startRelation(statement.relation, metadata, true); |
217 |
229 |
218 while (prepared->next()) { |
230 while (prepared->next()) { |
219 for (int i = 0; i < columnCount; i++) { |
231 for (int i = 0; i < columnCount; i++) { |
220 relationalWriter->writeAttribute(convertor.from_bytes(prepared->getString(i))); |
232 relationalWriter->writeAttribute(convertor.from_bytes(prepared->getString(i))); |