| 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 | |