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 // TODO: sqlite3_column_type |
106 relpipe::writer::TypeId getColumType(int columnIndex) { |
|
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 // fprintf(stderr, "%d → %s\n", columnIndex, type); |
|
111 // SELECT typeof(1+1); == "integer" |
|
112 // https://www.sqlite.org/c3ref/column_decltype.html – sqlite3_column_decltype |
|
113 // https://www.sqlite.org/c3ref/column_blob.html – sqlite3_column_type |
|
114 // 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 |
|
116 |
|
117 if (type == nullptr) return relpipe::writer::TypeId::STRING; |
|
118 else if (strcmp(type, "integer") == 0) return relpipe::writer::TypeId::INTEGER; |
|
119 else if (strcmp(type, "text") == 0) return relpipe::writer::TypeId::STRING; |
|
120 else return relpipe::writer::TypeId::STRING; |
|
121 // TODO: support also other data types |
|
122 } |
107 |
123 |
108 std::string getString(int columnIndex) { |
124 std::string getString(int columnIndex) { |
109 const char* value = (const char*) sqlite3_column_text(stmt, columnIndex); |
125 const char* value = (const char*) sqlite3_column_text(stmt, columnIndex); |
110 return value ? value : ""; // TODO: support NULL values (when supported in relpipe format) |
126 return value ? value : ""; // TODO: support NULL values (when supported in relpipe format) |
111 } |
127 } |
194 for (int i = 0; i < parameterCount; i++) { |
210 for (int i = 0; i < parameterCount; i++) { |
195 prepared->setString(i + 1, convertor.to_bytes(statement.parameters[i].value)); |
211 prepared->setString(i + 1, convertor.to_bytes(statement.parameters[i].value)); |
196 } |
212 } |
197 |
213 |
198 std::vector<relpipe::writer::AttributeMetadata> metadata; |
214 std::vector<relpipe::writer::AttributeMetadata> metadata; |
199 // TODO: support also other data types |
215 for (int i = 0; i < columnCount; i++) metadata.push_back({convertor.from_bytes(prepared->getColumName(i).c_str()), prepared->getColumType(i)}); |
200 for (int i = 0; i < columnCount; i++) metadata.push_back({convertor.from_bytes(prepared->getColumName(i).c_str()), relpipe::writer::TypeId::STRING}); |
|
201 relationalWriter->startRelation(statement.relation, metadata, true); |
216 relationalWriter->startRelation(statement.relation, metadata, true); |
202 |
217 |
203 while (prepared->next()) { |
218 while (prepared->next()) { |
204 for (int i = 0; i < columnCount; i++) { |
219 for (int i = 0; i < columnCount; i++) { |
205 relationalWriter->writeAttribute(convertor.from_bytes(prepared->getString(i))); |
220 relationalWriter->writeAttribute(convertor.from_bytes(prepared->getString(i))); |
346 // process optional SQL input |
361 // process optional SQL input |
347 processSqlInput(configuration.sqlAfterRelational); |
362 processSqlInput(configuration.sqlAfterRelational); |
348 |
363 |
349 // pass-through some relations: |
364 // pass-through some relations: |
350 for (const CopyRelations& copy : configuration.copyRelations) copyRelations(copy); |
365 for (const CopyRelations& copy : configuration.copyRelations) copyRelations(copy); |
351 |
366 |
352 connection->transactionCommit(); |
367 connection->transactionCommit(); |
353 |
368 |
354 // delete or keep the file: |
369 // delete or keep the file: |
355 if (configuration.file.size()) { |
370 if (configuration.file.size()) { |
356 if (configuration.keepFile == KeepFile::Never || (configuration.keepFile == KeepFile::Automatic && !fileAlreadyExisted)) { |
371 if (configuration.keepFile == KeepFile::Never || (configuration.keepFile == KeepFile::Automatic && !fileAlreadyExisted)) { |