| 1 | #include "sqlite_transfer.hpp" | 
|---|
| 2 |  | 
|---|
| 3 | #include "duckdb/common/types/date.hpp" | 
|---|
| 4 |  | 
|---|
| 5 | using namespace duckdb; | 
|---|
| 6 | using namespace std; | 
|---|
| 7 |  | 
|---|
| 8 | namespace sqlite { | 
|---|
| 9 |  | 
|---|
| 10 | bool TransferDatabase(Connection &con, sqlite3 *sqlite) { | 
|---|
| 11 | char *error; | 
|---|
| 12 | // start the SQLite transaction | 
|---|
| 13 | if (sqlite3_exec(sqlite, "BEGIN TRANSACTION", nullptr, nullptr, &error) != SQLITE_OK) { | 
|---|
| 14 | return false; | 
|---|
| 15 | } | 
|---|
| 16 |  | 
|---|
| 17 | // query the list of tables | 
|---|
| 18 | auto table_list = con.Query( "SELECT name, sql FROM sqlite_master();"); | 
|---|
| 19 |  | 
|---|
| 20 | for (size_t i = 0; i < table_list->collection.count; i++) { | 
|---|
| 21 | auto name = table_list->GetValue(0, i).ToString(); | 
|---|
| 22 | auto sql = table_list->GetValue(1, i).ToString(); | 
|---|
| 23 |  | 
|---|
| 24 | // for each table, first create the table in sqlite | 
|---|
| 25 | if (sqlite3_exec(sqlite, sql.c_str(), nullptr, nullptr, &error) != SQLITE_OK) { | 
|---|
| 26 | return false; | 
|---|
| 27 | } | 
|---|
| 28 |  | 
|---|
| 29 | // now transfer the actual data | 
|---|
| 30 | // first get the data from DuckDB | 
|---|
| 31 | auto result = con.Query( "SELECT * FROM "+ name); | 
|---|
| 32 | // create the prepared statement based on the result | 
|---|
| 33 | stringstream prepared; | 
|---|
| 34 | prepared << "INSERT INTO "<< name << " ("; | 
|---|
| 35 | for (size_t j = 0; j < result->types.size(); j++) { | 
|---|
| 36 | prepared << result->names[j]; | 
|---|
| 37 | if (j + 1 != result->types.size()) { | 
|---|
| 38 | prepared << ","; | 
|---|
| 39 | } | 
|---|
| 40 | } | 
|---|
| 41 | prepared << ") VALUES ("; | 
|---|
| 42 | for (size_t j = 0; j < result->types.size(); j++) { | 
|---|
| 43 | prepared << "?"; | 
|---|
| 44 | if (j + 1 != result->types.size()) { | 
|---|
| 45 | prepared << ","; | 
|---|
| 46 | } | 
|---|
| 47 | } | 
|---|
| 48 | prepared << ");"; | 
|---|
| 49 |  | 
|---|
| 50 | auto insert_statement = prepared.str(); | 
|---|
| 51 | sqlite3_stmt *stmt; | 
|---|
| 52 | if (sqlite3_prepare_v2(sqlite, insert_statement.c_str(), -1, &stmt, nullptr) != SQLITE_OK) { | 
|---|
| 53 | return false; | 
|---|
| 54 | } | 
|---|
| 55 |  | 
|---|
| 56 | auto &types = result->sql_types; | 
|---|
| 57 | for (size_t k = 0; k < result->collection.count; k++) { | 
|---|
| 58 | int rc = SQLITE_ERROR; | 
|---|
| 59 | for (size_t j = 0; j < types.size(); j++) { | 
|---|
| 60 | size_t bind_index = j + 1; | 
|---|
| 61 | auto value = result->GetValue(j, k); | 
|---|
| 62 | if (value.is_null) { | 
|---|
| 63 | rc = sqlite3_bind_null(stmt, bind_index); | 
|---|
| 64 | } else { | 
|---|
| 65 | // bind based on the type | 
|---|
| 66 | switch (types[j].id) { | 
|---|
| 67 | case SQLTypeId::BOOLEAN: | 
|---|
| 68 | case SQLTypeId::TINYINT: | 
|---|
| 69 | case SQLTypeId::SMALLINT: | 
|---|
| 70 | case SQLTypeId::INTEGER: | 
|---|
| 71 | rc = sqlite3_bind_int(stmt, bind_index, (int)value.GetValue<int64_t>()); | 
|---|
| 72 | break; | 
|---|
| 73 | case SQLTypeId::BIGINT: | 
|---|
| 74 | rc = sqlite3_bind_int64(stmt, bind_index, (sqlite3_int64)value.GetValue<int64_t>()); | 
|---|
| 75 | break; | 
|---|
| 76 | case SQLTypeId::DATE: { | 
|---|
| 77 | auto date_str = value.ToString() + " 00:00:00"; | 
|---|
| 78 | rc = sqlite3_bind_text(stmt, bind_index, date_str.c_str(), -1, SQLITE_TRANSIENT); | 
|---|
| 79 | break; | 
|---|
| 80 | } | 
|---|
| 81 | case SQLTypeId::TIMESTAMP: | 
|---|
| 82 | // TODO | 
|---|
| 83 | throw NotImplementedException( "Transferring timestamps is not supported yet"); | 
|---|
| 84 | case SQLTypeId::DECIMAL: | 
|---|
| 85 | rc = sqlite3_bind_double(stmt, bind_index, value.value_.double_); | 
|---|
| 86 | break; | 
|---|
| 87 | case SQLTypeId::VARCHAR: | 
|---|
| 88 | rc = sqlite3_bind_text(stmt, bind_index, value.ToString().c_str(), -1, SQLITE_TRANSIENT); | 
|---|
| 89 | break; | 
|---|
| 90 | default: | 
|---|
| 91 | break; | 
|---|
| 92 | } | 
|---|
| 93 | } | 
|---|
| 94 | if (rc != SQLITE_OK) { | 
|---|
| 95 | return false; | 
|---|
| 96 | } | 
|---|
| 97 | } | 
|---|
| 98 | rc = sqlite3_step(stmt); | 
|---|
| 99 | if (rc != SQLITE_DONE) { | 
|---|
| 100 | return false; | 
|---|
| 101 | } | 
|---|
| 102 | if (sqlite3_reset(stmt) != SQLITE_OK) { | 
|---|
| 103 | return false; | 
|---|
| 104 | } | 
|---|
| 105 | } | 
|---|
| 106 | sqlite3_finalize(stmt); | 
|---|
| 107 | } | 
|---|
| 108 | // commit the SQLite transaction | 
|---|
| 109 | if (sqlite3_exec(sqlite, "COMMIT", nullptr, nullptr, &error) != SQLITE_OK) { | 
|---|
| 110 | return false; | 
|---|
| 111 | } | 
|---|
| 112 | return true; | 
|---|
| 113 | } | 
|---|
| 114 |  | 
|---|
| 115 | unique_ptr<QueryResult> QueryDatabase(vector<SQLType> result_types, sqlite3 *sqlite, std::string query, | 
|---|
| 116 | volatile int &interrupt) { | 
|---|
| 117 | if (!sqlite) { | 
|---|
| 118 | return nullptr; | 
|---|
| 119 | } | 
|---|
| 120 | // prepare the SQL statement | 
|---|
| 121 | sqlite3_stmt *stmt; | 
|---|
| 122 | if (sqlite3_prepare_v2(sqlite, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) { | 
|---|
| 123 | return make_unique<MaterializedQueryResult>(sqlite3_errmsg(sqlite)); | 
|---|
| 124 | } | 
|---|
| 125 | int col_count = sqlite3_column_count(stmt); | 
|---|
| 126 | vector<string> names; | 
|---|
| 127 | for (int i = 0; i < col_count; i++) { | 
|---|
| 128 | names.push_back(sqlite3_column_name(stmt, i)); | 
|---|
| 129 | } | 
|---|
| 130 | // figure out the types of the columns | 
|---|
| 131 | // construct the types of the result | 
|---|
| 132 | vector<TypeId> typeids; | 
|---|
| 133 | for (auto &tp : result_types) { | 
|---|
| 134 | typeids.push_back(GetInternalType(tp)); | 
|---|
| 135 | } | 
|---|
| 136 |  | 
|---|
| 137 | // construct the result | 
|---|
| 138 | auto result = make_unique<MaterializedQueryResult>(StatementType::SELECT_STATEMENT, result_types, typeids, std::move(names)); | 
|---|
| 139 | DataChunk result_chunk; | 
|---|
| 140 | result_chunk.Initialize(typeids); | 
|---|
| 141 | int rc = SQLITE_ERROR; | 
|---|
| 142 | while ((rc = sqlite3_step(stmt)) == SQLITE_ROW && interrupt == 0) { | 
|---|
| 143 | // get the value for each of the columns | 
|---|
| 144 | idx_t result_idx = result_chunk.size(); | 
|---|
| 145 | for (int i = 0; i < col_count; i++) { | 
|---|
| 146 | if (sqlite3_column_type(stmt, i) == SQLITE_NULL) { | 
|---|
| 147 | // NULL value | 
|---|
| 148 | FlatVector::Nullmask(result_chunk.data[i])[result_idx] = true; | 
|---|
| 149 | } else { | 
|---|
| 150 | auto dataptr = FlatVector::GetData(result_chunk.data[i]); | 
|---|
| 151 | // normal value, convert type | 
|---|
| 152 | switch (result_types[i].id) { | 
|---|
| 153 | case SQLTypeId::BOOLEAN: | 
|---|
| 154 | ((int8_t *)dataptr)[result_idx] = sqlite3_column_int(stmt, i) == 0 ? 0 : 1; | 
|---|
| 155 | break; | 
|---|
| 156 | case SQLTypeId::TINYINT: | 
|---|
| 157 | ((int8_t *)dataptr)[result_idx] = (int8_t)sqlite3_column_int(stmt, i); | 
|---|
| 158 | break; | 
|---|
| 159 | case SQLTypeId::SMALLINT: | 
|---|
| 160 | ((int16_t *)dataptr)[result_idx] = (int16_t)sqlite3_column_int(stmt, i); | 
|---|
| 161 | break; | 
|---|
| 162 | case SQLTypeId::INTEGER: | 
|---|
| 163 | ((int32_t *)dataptr)[result_idx] = (int32_t)sqlite3_column_int(stmt, i); | 
|---|
| 164 | break; | 
|---|
| 165 | case SQLTypeId::BIGINT: | 
|---|
| 166 | ((int64_t *)dataptr)[result_idx] = (int64_t)sqlite3_column_int64(stmt, i); | 
|---|
| 167 | break; | 
|---|
| 168 | case SQLTypeId::DECIMAL: | 
|---|
| 169 | ((double *)dataptr)[result_idx] = (double)sqlite3_column_double(stmt, i); | 
|---|
| 170 | break; | 
|---|
| 171 | case SQLTypeId::VARCHAR: { | 
|---|
| 172 | Value result((char *)sqlite3_column_text(stmt, i)); | 
|---|
| 173 | result_chunk.SetValue(i, result_idx, result); | 
|---|
| 174 | break; | 
|---|
| 175 | } | 
|---|
| 176 | case SQLTypeId::DATE: { | 
|---|
| 177 | auto unix_time = sqlite3_column_int64(stmt, i); | 
|---|
| 178 | ((date_t *)dataptr)[result_idx] = Date::EpochToDate(unix_time); | 
|---|
| 179 | break; | 
|---|
| 180 | } | 
|---|
| 181 | default: | 
|---|
| 182 | throw NotImplementedException( "Unimplemented type for SQLite -> DuckDB type conversion"); | 
|---|
| 183 | } | 
|---|
| 184 | } | 
|---|
| 185 | } | 
|---|
| 186 | result_chunk.SetCardinality(result_idx + 1); | 
|---|
| 187 | if (result_chunk.size() == STANDARD_VECTOR_SIZE) { | 
|---|
| 188 | // chunk is filled | 
|---|
| 189 | // flush the chunk to the result | 
|---|
| 190 | result->collection.Append(result_chunk); | 
|---|
| 191 | result_chunk.Reset(); | 
|---|
| 192 | } | 
|---|
| 193 | } | 
|---|
| 194 | if (rc != SQLITE_DONE) { | 
|---|
| 195 | // failed | 
|---|
| 196 | return nullptr; | 
|---|
| 197 | } | 
|---|
| 198 | if (result_chunk.size() > 0) { | 
|---|
| 199 | // final append of any leftover data | 
|---|
| 200 | result->collection.Append(result_chunk); | 
|---|
| 201 | result_chunk.Reset(); | 
|---|
| 202 | } | 
|---|
| 203 | sqlite3_finalize(stmt); | 
|---|
| 204 | return move(result); | 
|---|
| 205 | } | 
|---|
| 206 |  | 
|---|
| 207 | }; // namespace sqlite | 
|---|
| 208 |  | 
|---|